diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..f2bfa724
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,13 @@
+# See: https://docs.github.com/en/code-security/supply-chain-security/configuration-options-for-dependency-updates#about-the-dependabotyml-file
+version: 2
+
+updates:
+  # Configure check for outdated GitHub Actions actions in workflows.
+  # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/dependabot/README.md
+  # See: https://docs.github.com/en/code-security/supply-chain-security/keeping-your-actions-up-to-date-with-dependabot
+  - package-ecosystem: github-actions
+    directory: / # Check the repository's workflows under /.github/workflows/
+    schedule:
+      interval: daily
+    labels:
+      - "topic: infrastructure"
diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml
index da41142d..f4ce9e1c 100644
--- a/.github/workflows/compile-examples.yml
+++ b/.github/workflows/compile-examples.yml
@@ -16,7 +16,7 @@ jobs:
           - STMicroelectronics:stm32:Nucleo_64:pnum=P_NUCLEO_WB55RG
 
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v4
       - uses: arduino/compile-sketches@v1
         with:
           github-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml
new file mode 100644
index 00000000..9cde1acc
--- /dev/null
+++ b/.github/workflows/sync-labels.yml
@@ -0,0 +1,138 @@
+# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/sync-labels.md
+name: Sync Labels
+
+# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows
+on:
+  push:
+    paths:
+      - ".github/workflows/sync-labels.ya?ml"
+      - ".github/label-configuration-files/*.ya?ml"
+  pull_request:
+    paths:
+      - ".github/workflows/sync-labels.ya?ml"
+      - ".github/label-configuration-files/*.ya?ml"
+  schedule:
+    # Run daily at 8 AM UTC to sync with changes to shared label configurations.
+    - cron: "0 8 * * *"
+  workflow_dispatch:
+  repository_dispatch:
+
+env:
+  CONFIGURATIONS_FOLDER: .github/label-configuration-files
+  CONFIGURATIONS_ARTIFACT: label-configuration-files
+
+jobs:
+  check:
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Download JSON schema for labels configuration file
+        id: download-schema
+        uses: carlosperate/download-file-action@v2
+        with:
+          file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json
+          location: ${{ runner.temp }}/label-configuration-schema
+
+      - name: Install JSON schema validator
+        run: |
+          sudo npm install \
+            --global \
+            ajv-cli \
+            ajv-formats
+
+      - name: Validate local labels configuration
+        run: |
+          # See: https://github.com/ajv-validator/ajv-cli#readme
+          ajv validate \
+            --all-errors \
+            -c ajv-formats \
+            -s "${{ steps.download-schema.outputs.file-path }}" \
+            -d "${{ env.CONFIGURATIONS_FOLDER }}/*.{yml,yaml}"
+
+  download:
+    needs: check
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        filename:
+          # Filenames of the shared configurations to apply to the repository in addition to the local configuration.
+          # https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/sync-labels
+          - universal.yml
+
+    steps:
+      - name: Download
+        uses: carlosperate/download-file-action@v2
+        with:
+          file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }}
+
+      - name: Pass configuration files to next job via workflow artifact
+        uses: actions/upload-artifact@v3
+        with:
+          path: |
+            *.yaml
+            *.yml
+          if-no-files-found: error
+          name: ${{ env.CONFIGURATIONS_ARTIFACT }}
+
+  sync:
+    needs: download
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Set environment variables
+        run: |
+          # See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
+          echo "MERGED_CONFIGURATION_PATH=${{ runner.temp }}/labels.yml" >> "$GITHUB_ENV"
+
+      - name: Determine whether to dry run
+        id: dry-run
+        if: >
+          github.event_name == 'pull_request' ||
+          (
+            (
+              github.event_name == 'push' ||
+              github.event_name == 'workflow_dispatch'
+            ) &&
+            github.ref != format('refs/heads/{0}', github.event.repository.default_branch)
+          )
+        run: |
+          # Use of this flag in the github-label-sync command will cause it to only check the validity of the
+          # configuration.
+          echo "::set-output name=flag::--dry-run"
+
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Download configuration files artifact
+        uses: actions/download-artifact@v3
+        with:
+          name: ${{ env.CONFIGURATIONS_ARTIFACT }}
+          path: ${{ env.CONFIGURATIONS_FOLDER }}
+
+      - name: Remove unneeded artifact
+        uses: geekyeggo/delete-artifact@v2
+        with:
+          name: ${{ env.CONFIGURATIONS_ARTIFACT }}
+
+      - name: Merge label configuration files
+        run: |
+          # Merge all configuration files
+          shopt -s extglob
+          cat "${{ env.CONFIGURATIONS_FOLDER }}"/*.@(yml|yaml) > "${{ env.MERGED_CONFIGURATION_PATH }}"
+
+      - name: Install github-label-sync
+        run: sudo npm install --global github-label-sync
+
+      - name: Sync labels
+        env:
+          GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: |
+          # See: https://github.com/Financial-Times/github-label-sync
+          github-label-sync \
+            --labels "${{ env.MERGED_CONFIGURATION_PATH }}" \
+            ${{ steps.dry-run.outputs.flag }} \
+            ${{ github.repository }}
diff --git a/examples/Central/LedControl/LedControl.ino b/examples/Central/LedControl/LedControl.ino
index 08db7aca..af1265d7 100644
--- a/examples/Central/LedControl/LedControl.ino
+++ b/examples/Central/LedControl/LedControl.ino
@@ -1,9 +1,9 @@
 /*
   LED Control
 
-  This example scans for BLE peripherals until one with the advertised service
+  This example scans for Bluetooth® Low Energy peripherals until one with the advertised service
   "19b10000-e8f2-537e-4f6c-d104768a1214" UUID is found. Once discovered and connected,
-  it will remotely control the BLE Peripheral's LED, when the button is pressed or released.
+  it will remotely control the Bluetooth® Low Energy peripheral's LED, when the button is pressed or released.
 
   The circuit:
   - Boards with integrated BLE or Nucleo board plus one of BLE X-Nucleo shield::
@@ -119,14 +119,14 @@ void setup() {
   // configure the button pin as input
   pinMode(buttonPin, INPUT_PULLUP);
 
-  // initialize the BLE hardware
+  // initialize the Bluetooth® Low Energy hardware
   BLE.begin();
 
   // Get initial button state
   initialButtonState = digitalRead(buttonPin);
   oldButtonState = initialButtonState;
 
-  Serial.println("BLE Central - LED control");
+  Serial.println("Bluetooth® Low Energy Central - LED control");
 
   // start scanning for peripherals
   int ret = 1;
diff --git a/examples/Central/PeripheralExplorer/PeripheralExplorer.ino b/examples/Central/PeripheralExplorer/PeripheralExplorer.ino
index 44aa00c4..44896813 100644
--- a/examples/Central/PeripheralExplorer/PeripheralExplorer.ino
+++ b/examples/Central/PeripheralExplorer/PeripheralExplorer.ino
@@ -1,7 +1,7 @@
 /*
   Peripheral Explorer
 
-  This example scans for BLE peripherals until one with a particular name ("LED")
+  This example scans for Bluetooth® Low Energy peripherals until one with a particular name ("LED")
   is found. Then connects, and discovers + prints all the peripheral's attributes.
 
   The circuit:
@@ -108,12 +108,12 @@ void setup() {
 
   // begin initialization
   if (!BLE.begin()) {
-    Serial.println("starting BLE failed!");
+    Serial.println("starting Bluetooth® Low Energy module failed!");
 
     while (1);
   }
 
-  Serial.println("BLE Central - Peripheral Explorer");
+  Serial.println("Bluetooth® Low Energy Central - Peripheral Explorer");
 
   // start scanning for peripherals
   int ret = 1;
diff --git a/examples/Central/Scan/Scan.ino b/examples/Central/Scan/Scan.ino
index 4c34e563..551c54b3 100644
--- a/examples/Central/Scan/Scan.ino
+++ b/examples/Central/Scan/Scan.ino
@@ -1,7 +1,7 @@
 /*
   Scan
 
-  This example scans for BLE peripherals and prints out their advertising details:
+  This example scans for Bluetooth® Low Energy peripherals and prints out their advertising details:
   address, local name, advertised service UUID's.
 
   The circuit:
@@ -104,12 +104,12 @@ void setup() {
 
   // begin initialization
   if (!BLE.begin()) {
-    Serial.println("starting BLE failed!");
+    Serial.println("starting Bluetooth® Low Energy module failed!");
 
     while (1);
   }
 
-  Serial.println("BLE Central scan");
+  Serial.println("Bluetooth® Low Energy Central scan");
 
   // start scanning for peripherals
   int ret = 1;
diff --git a/examples/Central/ScanCallback/ScanCallback.ino b/examples/Central/ScanCallback/ScanCallback.ino
index bba10789..81f34879 100644
--- a/examples/Central/ScanCallback/ScanCallback.ino
+++ b/examples/Central/ScanCallback/ScanCallback.ino
@@ -1,7 +1,7 @@
 /*
   Scan Callback
 
-  This example scans for BLE peripherals and prints out their advertising details:
+  This example scans for Bluetooth® Low Energy peripherals and prints out their advertising details:
   address, local name, advertised service UUIDs. Unlike the Scan example, it uses
   the callback style APIs and disables filtering so the peripheral discovery is
   reported for every single advertisement it makes.
@@ -106,12 +106,12 @@ void setup() {
 
   // begin initialization
   if (!BLE.begin()) {
-    Serial.println("starting BLE failed!");
+    Serial.println("starting Bluetooth® Low Energy module failed!");
 
     while (1);
   }
 
-  Serial.println("BLE Central scan callback");
+  Serial.println("Bluetooth® Low Energy Central scan callback");
 
   // set the discovered event handle
   BLE.setEventHandler(BLEDiscovered, bleCentralDiscoverHandler);
diff --git a/examples/Central/SensorTagButton/SensorTagButton.ino b/examples/Central/SensorTagButton/SensorTagButton.ino
index b371617f..e3d563f7 100644
--- a/examples/Central/SensorTagButton/SensorTagButton.ino
+++ b/examples/Central/SensorTagButton/SensorTagButton.ino
@@ -1,7 +1,7 @@
 /*
   SensorTag Button
 
-  This example scans for BLE peripherals until a TI SensorTag is discovered.
+  This example scans for Bluetooth® Low Energy peripherals until a TI SensorTag is discovered.
   It then connects to it, discovers the attributes of the 0xffe0 service,
   subscribes to the Simple Key Characteristic (UUID 0xffe1). When a button is
   pressed on the SensorTag a notification is received and the button state is
@@ -108,12 +108,12 @@ void setup() {
 
   // begin initialization
   if (!BLE.begin()) {
-    Serial.println("starting BLE failed!");
+    Serial.println("starting Bluetooth® Low Energy module failed!");
 
     while (1);
   }
 
-  Serial.println("BLE Central - SensorTag button");
+  Serial.println("Bluetooth® Low Energy Central - SensorTag button");
   Serial.println("Make sure to turn on the device.");
 
   // start scanning for peripherals
diff --git a/examples/Peripheral/Advertising/EnhancedAdvertising/EnhancedAdvertising.ino b/examples/Peripheral/Advertising/EnhancedAdvertising/EnhancedAdvertising.ino
new file mode 100644
index 00000000..88ee8acf
--- /dev/null
+++ b/examples/Peripheral/Advertising/EnhancedAdvertising/EnhancedAdvertising.ino
@@ -0,0 +1,120 @@
+#include <STM32duinoBLE.h>
+#include <STM32duinoBLE.h>
+
+#if defined(ARDUINO_STEVAL_MKBOXPRO)
+/* STEVAL-MKBOXPRO */
+SPIClass SpiHCI(PA7, PA6, PA5);
+HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_LP, PA2, PB11, PD4, 1000000, SPI_MODE3);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#elif defined(ARDUINO_STEVAL_MKSBOX1V1)
+/* STEVAL-MKSBOX1V1 */
+SPIClass SpiHCI(PC3, PD3, PD1);
+HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_1S, PD0, PD4, PA8, 1000000, SPI_MODE1);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#elif defined(ARDUINO_B_L475E_IOT01A) || defined(ARDUINO_B_L4S5I_IOT01A)
+/* B-L475E-IOT01A1 or B_L4S5I_IOT01A */
+SPIClass SpiHCI(PC12, PC11, PC10);
+HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, PD13, PE6, PA8, 8000000, SPI_MODE0);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#elif defined(ARDUINO_NUCLEO_WB15CC) || defined(ARDUINO_P_NUCLEO_WB55RG) || \
+    defined(ARDUINO_STM32WB5MM_DK) || defined(P_NUCLEO_WB55_USB_DONGLE)
+HCISharedMemTransportClass HCISharedMemTransport;
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISharedMemTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#else
+/* Shield IDB05A2 with SPI clock on D3 */
+SPIClass SpiHCI(D11, D12, D3);
+HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M0, A1, A0, D7, 8000000, SPI_MODE0);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+/* Shield IDB05A2 with SPI clock on D13 */
+// #define SpiHCI SPI
+// HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M0, A1, A0, D7, 8000000, SPI_MODE0);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield IDB05A1 with SPI clock on D3 */
+// SPIClass SpiHCI(D11, D12, D3);
+// HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, A1, A0, D7, 8000000, SPI_MODE0);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield IDB05A1 with SPI clock on D13 */
+// #define SpiHCI SPI
+// HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, A1, A0, D7, 8000000, SPI_MODE0);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield BNRG2A1 with SPI clock on D3 */
+// SPIClass SpiHCI(D11, D12, D3);
+// HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M2SP, A1, A0, D7, 1000000, SPI_MODE1);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield BNRG2A1 with SPI clock on D13 */
+// #define SpiHCI SPI
+// HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M2SP, A1, A0, D7, 1000000, SPI_MODE1);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+#endif
+BLEService myService("fff0");
+BLEIntCharacteristic myCharacteristic("fff1", BLERead | BLEBroadcast);
+
+// Advertising parameters should have a global scope. Do NOT define them in 'setup' or in 'loop'
+const uint8_t manufactData[4] = {0x01, 0x02, 0x03, 0x04};
+const uint8_t serviceData[3] = {0x00, 0x01, 0x02};
+
+void setup() {
+  Serial.begin(9600);
+  while (!Serial);
+
+  if (!BLE.begin()) {
+    Serial.println("failed to initialize BLE!");
+    while (1);
+  }
+
+  myService.addCharacteristic(myCharacteristic);
+  BLE.addService(myService);
+
+  // Build scan response data packet
+  BLEAdvertisingData scanData;
+  // Set parameters for scan response packet
+  scanData.setLocalName("Test enhanced advertising");
+  // Copy set parameters in the actual scan response packet
+  BLE.setScanResponseData(scanData);
+
+  // Build advertising data packet
+  BLEAdvertisingData advData;
+  // Set parameters for advertising packet
+  advData.setManufacturerData(0x004C, manufactData, sizeof(manufactData));
+  advData.setAdvertisedService(myService);
+  advData.setAdvertisedServiceData(0xfff0, serviceData, sizeof(serviceData));
+  // Copy set parameters in the actual advertising packet
+  BLE.setAdvertisingData(advData);
+
+  BLE.advertise();
+  Serial.println("advertising ...");
+}
+
+void loop() {
+  BLE.poll();
+}
diff --git a/examples/Peripheral/Advertising/RawDataAdvertising/RawDataAdvertising.ino b/examples/Peripheral/Advertising/RawDataAdvertising/RawDataAdvertising.ino
new file mode 100644
index 00000000..505ce5c4
--- /dev/null
+++ b/examples/Peripheral/Advertising/RawDataAdvertising/RawDataAdvertising.ino
@@ -0,0 +1,117 @@
+#include <STM32duinoBLE.h>
+#include <STM32duinoBLE.h>
+
+#if defined(ARDUINO_STEVAL_MKBOXPRO)
+/* STEVAL-MKBOXPRO */
+SPIClass SpiHCI(PA7, PA6, PA5);
+HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_LP, PA2, PB11, PD4, 1000000, SPI_MODE3);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#elif defined(ARDUINO_STEVAL_MKSBOX1V1)
+/* STEVAL-MKSBOX1V1 */
+SPIClass SpiHCI(PC3, PD3, PD1);
+HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_1S, PD0, PD4, PA8, 1000000, SPI_MODE1);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#elif defined(ARDUINO_B_L475E_IOT01A) || defined(ARDUINO_B_L4S5I_IOT01A)
+/* B-L475E-IOT01A1 or B_L4S5I_IOT01A */
+SPIClass SpiHCI(PC12, PC11, PC10);
+HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, PD13, PE6, PA8, 8000000, SPI_MODE0);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#elif defined(ARDUINO_NUCLEO_WB15CC) || defined(ARDUINO_P_NUCLEO_WB55RG) || \
+    defined(ARDUINO_STM32WB5MM_DK) || defined(P_NUCLEO_WB55_USB_DONGLE)
+HCISharedMemTransportClass HCISharedMemTransport;
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISharedMemTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#else
+/* Shield IDB05A2 with SPI clock on D3 */
+SPIClass SpiHCI(D11, D12, D3);
+HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M0, A1, A0, D7, 8000000, SPI_MODE0);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+/* Shield IDB05A2 with SPI clock on D13 */
+// #define SpiHCI SPI
+// HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M0, A1, A0, D7, 8000000, SPI_MODE0);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield IDB05A1 with SPI clock on D3 */
+// SPIClass SpiHCI(D11, D12, D3);
+// HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, A1, A0, D7, 8000000, SPI_MODE0);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield IDB05A1 with SPI clock on D13 */
+// #define SpiHCI SPI
+// HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, A1, A0, D7, 8000000, SPI_MODE0);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield BNRG2A1 with SPI clock on D3 */
+// SPIClass SpiHCI(D11, D12, D3);
+// HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M2SP, A1, A0, D7, 1000000, SPI_MODE1);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield BNRG2A1 with SPI clock on D13 */
+// #define SpiHCI SPI
+// HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M2SP, A1, A0, D7, 1000000, SPI_MODE1);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+#endif
+BLEService myService("fff0");
+BLEIntCharacteristic myCharacteristic("fff1", BLERead | BLEBroadcast);
+
+// Advertising parameters should have a global scope. Do NOT define them in 'setup' or in 'loop'
+const uint8_t completeRawAdvertisingData[] = {0x02,0x01,0x06,0x09,0xff,0x01,0x01,0x00,0x01,0x02,0x03,0x04,0x05};   
+
+void setup() {
+  Serial.begin(9600);
+  while (!Serial);
+
+  if (!BLE.begin()) {
+    Serial.println("failed to initialize BLE!");
+    while (1);
+  }
+
+  myService.addCharacteristic(myCharacteristic);
+  BLE.addService(myService);
+
+  // Build advertising data packet
+  BLEAdvertisingData advData;
+  // If a packet has a raw data parameter, then all the other parameters of the packet will be ignored
+  advData.setRawData(completeRawAdvertisingData, sizeof(completeRawAdvertisingData));  
+  // Copy set parameters in the actual advertising packet
+  BLE.setAdvertisingData(advData);
+
+  // Build scan response data packet
+  BLEAdvertisingData scanData;
+  scanData.setLocalName("Test advertising raw data");
+  // Copy set parameters in the actual scan response packet
+  BLE.setScanResponseData(scanData);
+  
+  BLE.advertise();
+
+  Serial.println("advertising ...");
+}
+
+void loop() {
+  BLE.poll();
+}
diff --git a/examples/Peripheral/BatteryMonitor/BatteryMonitor.ino b/examples/Peripheral/BatteryMonitor/BatteryMonitor.ino
new file mode 100644
index 00000000..1a2cd63f
--- /dev/null
+++ b/examples/Peripheral/BatteryMonitor/BatteryMonitor.ino
@@ -0,0 +1,182 @@
+/*
+  Battery Monitor
+
+  This example creates a Bluetooth® Low Energy peripheral with the standard battery service and
+  level characteristic. The A0 pin is used to calculate the battery level.
+
+  The circuit:
+  - Arduino MKR WiFi 1010, Arduino Uno WiFi Rev2 board, Arduino Nano 33 IoT,
+    Arduino Nano 33 BLE, or Arduino Nano 33 BLE Sense board.
+
+  You can use a generic Bluetooth® Low Energy central app, like LightBlue (iOS and Android) or
+  nRF Connect (Android), to interact with the services and characteristics
+  created in this sketch.
+
+  This example code is in the public domain.
+*/
+
+#include <STM32duinoBLE.h>
+#include <STM32duinoBLE.h>
+
+#if defined(ARDUINO_STEVAL_MKBOXPRO)
+/* STEVAL-MKBOXPRO */
+SPIClass SpiHCI(PA7, PA6, PA5);
+HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_LP, PA2, PB11, PD4, 1000000, SPI_MODE3);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#elif defined(ARDUINO_STEVAL_MKSBOX1V1)
+/* STEVAL-MKSBOX1V1 */
+SPIClass SpiHCI(PC3, PD3, PD1);
+HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_1S, PD0, PD4, PA8, 1000000, SPI_MODE1);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#elif defined(ARDUINO_B_L475E_IOT01A) || defined(ARDUINO_B_L4S5I_IOT01A)
+/* B-L475E-IOT01A1 or B_L4S5I_IOT01A */
+SPIClass SpiHCI(PC12, PC11, PC10);
+HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, PD13, PE6, PA8, 8000000, SPI_MODE0);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#elif defined(ARDUINO_NUCLEO_WB15CC) || defined(ARDUINO_P_NUCLEO_WB55RG) || \
+    defined(ARDUINO_STM32WB5MM_DK) || defined(P_NUCLEO_WB55_USB_DONGLE)
+HCISharedMemTransportClass HCISharedMemTransport;
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISharedMemTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#else
+/* Shield IDB05A2 with SPI clock on D3 */
+SPIClass SpiHCI(D11, D12, D3);
+HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M0, A1, A0, D7, 8000000, SPI_MODE0);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+/* Shield IDB05A2 with SPI clock on D13 */
+// #define SpiHCI SPI
+// HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M0, A1, A0, D7, 8000000, SPI_MODE0);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield IDB05A1 with SPI clock on D3 */
+// SPIClass SpiHCI(D11, D12, D3);
+// HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, A1, A0, D7, 8000000, SPI_MODE0);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield IDB05A1 with SPI clock on D13 */
+// #define SpiHCI SPI
+// HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, A1, A0, D7, 8000000, SPI_MODE0);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield BNRG2A1 with SPI clock on D3 */
+// SPIClass SpiHCI(D11, D12, D3);
+// HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M2SP, A1, A0, D7, 1000000, SPI_MODE1);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield BNRG2A1 with SPI clock on D13 */
+// #define SpiHCI SPI
+// HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M2SP, A1, A0, D7, 1000000, SPI_MODE1);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+#endif
+// Bluetooth® Low Energy Battery Service
+BLEService batteryService("180F");
+
+// Bluetooth® Low Energy Battery Level Characteristic
+BLEUnsignedCharCharacteristic batteryLevelChar("2A19",  // standard 16-bit characteristic UUID
+    BLERead | BLENotify); // remote clients will be able to get notifications if this characteristic changes
+
+int oldBatteryLevel = 0;  // last battery level reading from analog input
+long previousMillis = 0;  // last time the battery level was checked, in ms
+
+void setup() {
+  Serial.begin(9600);    // initialize serial communication
+  while (!Serial);
+
+  pinMode(LED_BUILTIN, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected
+
+  // begin initialization
+  if (!BLE.begin()) {
+    Serial.println("starting BLE failed!");
+
+    while (1);
+  }
+
+  /* Set a local name for the Bluetooth® Low Energy device
+     This name will appear in advertising packets
+     and can be used by remote devices to identify this Bluetooth® Low Energy device
+     The name can be changed but maybe be truncated based on space left in advertisement packet
+  */
+  BLE.setLocalName("BatteryMonitor");
+  BLE.setAdvertisedService(batteryService); // add the service UUID
+  batteryService.addCharacteristic(batteryLevelChar); // add the battery level characteristic
+  BLE.addService(batteryService); // Add the battery service
+  batteryLevelChar.writeValue(oldBatteryLevel); // set initial value for this characteristic
+
+  /* Start advertising Bluetooth® Low Energy.  It will start continuously transmitting Bluetooth® Low Energy
+     advertising packets and will be visible to remote Bluetooth® Low Energy central devices
+     until it receives a new connection */
+
+  // start advertising
+  BLE.advertise();
+
+  Serial.println("Bluetooth® device active, waiting for connections...");
+}
+
+void loop() {
+  // wait for a Bluetooth® Low Energy central
+  BLEDevice central = BLE.central();
+
+  // if a central is connected to the peripheral:
+  if (central) {
+    Serial.print("Connected to central: ");
+    // print the central's BT address:
+    Serial.println(central.address());
+    // turn on the LED to indicate the connection:
+    digitalWrite(LED_BUILTIN, HIGH);
+
+    // check the battery level every 200ms
+    // while the central is connected:
+    while (central.connected()) {
+      long currentMillis = millis();
+      // if 200ms have passed, check the battery level:
+      if (currentMillis - previousMillis >= 200) {
+        previousMillis = currentMillis;
+        updateBatteryLevel();
+      }
+    }
+    // when the central disconnects, turn off the LED:
+    digitalWrite(LED_BUILTIN, LOW);
+    Serial.print("Disconnected from central: ");
+    Serial.println(central.address());
+  }
+}
+
+void updateBatteryLevel() {
+  /* Read the current voltage level on the A0 analog input pin.
+     This is used here to simulate the charge level of a battery.
+  */
+  int battery = analogRead(A0);
+  int batteryLevel = map(battery, 0, 1023, 0, 100);
+
+  if (batteryLevel != oldBatteryLevel) {      // if the battery level has changed
+    Serial.print("Battery Level % is now: "); // print it
+    Serial.println(batteryLevel);
+    batteryLevelChar.writeValue(batteryLevel);  // and update the battery level characteristic
+    oldBatteryLevel = batteryLevel;           // save the level for next comparison
+  }
+}
diff --git a/examples/Peripheral/ButtonLED/ButtonLED.ino b/examples/Peripheral/ButtonLED/ButtonLED.ino
index fb186766..a733a2b1 100644
--- a/examples/Peripheral/ButtonLED/ButtonLED.ino
+++ b/examples/Peripheral/ButtonLED/ButtonLED.ino
@@ -1,7 +1,7 @@
 /*
   Button LED
 
-  This example creates a BLE peripheral with service that contains a
+  This example creates a Bluetooth® Low Energy peripheral with service that contains a
   characteristic to control an LED and another characteristic that
   represents the state of the button.
 
@@ -18,7 +18,7 @@
     - X-NUCLEO-IDB05A1
     - X-NUCLEO-BNRG2A1
 
-  You can use a generic BLE central app, like LightBlue (iOS and Android) or
+  You can use a generic Bluetooth® Low Energy central app, like LightBlue (iOS and Android) or
   nRF Connect (Android), to interact with the services and characteristics
   created in this sketch.
 
@@ -127,7 +127,7 @@ void setup() {
 
   // begin initialization
   if (!BLE.begin()) {
-    Serial.println("starting BLE failed!");
+    Serial.println("starting Bluetooth® Low Energy module failed!");
 
     while (1);
   }
@@ -150,11 +150,11 @@ void setup() {
   // start advertising
   BLE.advertise();
 
-  Serial.println("Bluetooth device active, waiting for connections...");
+  Serial.println("Bluetooth® device active, waiting for connections...");
 }
 
 void loop() {
-  // poll for BLE events
+  // poll for Bluetooth® Low Energy events
   BLE.poll();
 
   // read the current button pin state
diff --git a/examples/Peripheral/CallbackLED/CallbackLED.ino b/examples/Peripheral/CallbackLED/CallbackLED.ino
index 0326f732..74c4de0e 100644
--- a/examples/Peripheral/CallbackLED/CallbackLED.ino
+++ b/examples/Peripheral/CallbackLED/CallbackLED.ino
@@ -1,7 +1,7 @@
 /*
   Callback LED
 
-  This example creates a BLE peripheral with service that contains a
+  This example creates a Bluetooth® Low Energy peripheral with service that contains a
   characteristic to control an LED. The callback features of the
   library are used.
 
@@ -18,7 +18,7 @@
     - X-NUCLEO-IDB05A1
     - X-NUCLEO-BNRG2A1
 
-  You can use a generic BLE central app, like LightBlue (iOS and Android) or
+  You can use a generic Bluetooth® Low Energy central app, like LightBlue (iOS and Android) or
   nRF Connect (Android), to interact with the services and characteristics
   created in this sketch.
 
@@ -118,7 +118,7 @@ void setup() {
 
   // begin initialization
   if (!BLE.begin()) {
-    Serial.println("starting BLE failed!");
+    Serial.println("starting Bluetooth® Low Energy module failed!");
 
     while (1);
   }
@@ -146,11 +146,11 @@ void setup() {
   // start advertising
   BLE.advertise();
 
-  Serial.println(("Bluetooth device active, waiting for connections..."));
+  Serial.println(("Bluetooth® device active, waiting for connections..."));
 }
 
 void loop() {
-  // poll for BLE events
+  // poll for Bluetooth® Low Energy events
   BLE.poll();
 }
 
diff --git a/examples/Peripheral/EncryptedBatteryMonitor/EncryptedBatteryMonitor.ino b/examples/Peripheral/EncryptedBatteryMonitor/EncryptedBatteryMonitor.ino
new file mode 100644
index 00000000..432068fb
--- /dev/null
+++ b/examples/Peripheral/EncryptedBatteryMonitor/EncryptedBatteryMonitor.ino
@@ -0,0 +1,340 @@
+/*
+  Battery Monitor
+
+  This example creates a BLE peripheral with the standard battery service and
+  level characteristic. The A0 pin is used to calculate the battery level.
+
+  The circuit:
+  - Arduino MKR WiFi 1010, Arduino Uno WiFi Rev2 board, Arduino Nano 33 IoT,
+    Arduino Nano 33 BLE, or Arduino Nano 33 BLE Sense board.
+
+  You can use a generic BLE central app, like LightBlue (iOS and Android) or
+  nRF Connect (Android), to interact with the services and characteristics
+  created in this sketch.
+
+  This example code is in the public domain.
+*/
+
+#include <STM32duinoBLE.h>
+
+
+#define PAIR_BUTTON 3        // button for pairing
+#define PAIR_LED 24           // LED used to signal pairing
+#define PAIR_LED_ON LOW       // Blue LED on Nano BLE has inverted logic
+#define PAIR_INTERVAL 30000   // interval for pairing after button press in ms
+
+#define CTRL_LED LED_BUILTIN
+
+#if defined(ARDUINO_STEVAL_MKBOXPRO)
+    /* STEVAL-MKBOXPRO */
+    SPIClass SpiHCI(PA7, PA6, PA5);
+HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_LP, PA2, PB11, PD4, 1000000, SPI_MODE3);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#elif defined(ARDUINO_STEVAL_MKSBOX1V1)
+    /* STEVAL-MKSBOX1V1 */
+    SPIClass SpiHCI(PC3, PD3, PD1);
+HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_1S, PD0, PD4, PA8, 1000000, SPI_MODE1);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#elif defined(ARDUINO_B_L475E_IOT01A) || defined(ARDUINO_B_L4S5I_IOT01A)
+    /* B-L475E-IOT01A1 or B_L4S5I_IOT01A */
+    SPIClass SpiHCI(PC12, PC11, PC10);
+HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, PD13, PE6, PA8, 8000000, SPI_MODE0);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#elif defined(ARDUINO_NUCLEO_WB15CC) || defined(ARDUINO_P_NUCLEO_WB55RG) || \
+    defined(ARDUINO_STM32WB5MM_DK) || defined(P_NUCLEO_WB55_USB_DONGLE)
+    HCISharedMemTransportClass HCISharedMemTransport;
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISharedMemTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+#else
+    /* Shield IDB05A2 with SPI clock on D3 */
+    SPIClass SpiHCI(D11, D12, D3);
+HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M0, A1, A0, D7, 8000000, SPI_MODE0);
+#if !defined(FAKE_BLELOCALDEVICE)
+BLELocalDevice BLEObj(&HCISpiTransport);
+BLELocalDevice &BLE = BLEObj;
+#endif
+/* Shield IDB05A2 with SPI clock on D13 */
+// #define SpiHCI SPI
+// HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M0, A1, A0, D7, 8000000, SPI_MODE0);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield IDB05A1 with SPI clock on D3 */
+// SPIClass SpiHCI(D11, D12, D3);
+// HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, A1, A0, D7, 8000000, SPI_MODE0);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield IDB05A1 with SPI clock on D13 */
+// #define SpiHCI SPI
+// HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, A1, A0, D7, 8000000, SPI_MODE0);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield BNRG2A1 with SPI clock on D3 */
+// SPIClass SpiHCI(D11, D12, D3);
+// HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M2SP, A1, A0, D7, 1000000, SPI_MODE1);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+/* Shield BNRG2A1 with SPI clock on D13 */
+// #define SpiHCI SPI
+// HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M2SP, A1, A0, D7, 1000000, SPI_MODE1);
+// #if !defined(FAKE_BLELOCALDEVICE)
+// BLELocalDevice BLEObj(&HCISpiTransport);
+// BLELocalDevice& BLE = BLEObj;
+// #endif
+#endif
+
+// BLE Battery Service
+BLEService batteryService("180F");
+
+// BLE Battery Level Characteristic
+BLEUnsignedCharCharacteristic batteryLevelChar("2A19",  // standard 16-bit characteristic UUID
+    BLERead | BLENotify); // remote clients will be able to get notifications if this characteristic changes
+BLEStringCharacteristic stringcharacteristic("183E", BLERead | BLEWrite, 31);
+
+
+// Add BLEEncryption tag to require pairing. This controls the LED.
+BLEUnsignedCharCharacteristic secretValue("2a3F", BLERead | BLEWrite | BLEEncryption);
+
+int oldBatteryLevel = 0;  // last battery level reading from analog input
+unsigned long previousMillis = 0;  // last time the battery level was checked, in ms
+unsigned long pairingStarted = 0;  // pairing start time when button is pressed
+bool wasConnected = 0;
+bool acceptOrReject = true;
+
+void setup() {
+  Serial.begin(9600);    // initialize serial communication
+  while (!Serial);
+
+  pinMode(CTRL_LED, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected
+  pinMode(PAIR_LED, OUTPUT);
+  pinMode(PAIR_BUTTON, INPUT_PULLUP);
+
+
+  Serial.println("Serial connected");
+  
+  // Callback function with confirmation code when new device is pairing.
+  BLE.setDisplayCode([](uint32_t confirmCode){
+    Serial.println("New device pairing request.");
+    Serial.print("Confirm code matches pairing device: ");
+    char code[6];
+    sprintf(code, "%06d", confirmCode);
+    Serial.println(code);
+  });
+  
+  // Callback to allow accepting or rejecting pairing
+  BLE.setBinaryConfirmPairing([&acceptOrReject](){
+    Serial.print("Should we confirm pairing? ");
+    delay(5000);
+    if(acceptOrReject){
+      acceptOrReject = false;
+      Serial.println("yes");
+      return true;
+    }else{
+      acceptOrReject = true;
+      Serial.println("no");
+      return false;
+    }
+  });
+
+  // IRKs are keys that identify the true owner of a random mac address.
+  // Add IRKs of devices you are bonded with.
+  BLE.setGetIRKs([](uint8_t* nIRKs, uint8_t** BDaddrTypes, uint8_t*** BDAddrs, uint8_t*** IRKs){
+    // Set to number of devices
+    *nIRKs       = 2;
+
+    *BDAddrs     = new uint8_t*[*nIRKs];
+    *IRKs        = new uint8_t*[*nIRKs];
+    *BDaddrTypes = new uint8_t[*nIRKs];
+
+    // Set these to the mac and IRK for your bonded devices as printed in the serial console after bonding.
+    uint8_t device1Mac[6]    = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    uint8_t device1IRK[16]   = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+    uint8_t device2Mac[6]    = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    uint8_t device2IRK[16]   = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+
+    (*BDaddrTypes)[0] = 0; // Type 0 is for pubc address, type 1 is for static random
+    (*BDAddrs)[0] = new uint8_t[6]; 
+    (*IRKs)[0]    = new uint8_t[16];
+    memcpy((*IRKs)[0]   , device1IRK,16);
+    memcpy((*BDAddrs)[0], device1Mac, 6);
+
+
+    (*BDaddrTypes)[1] = 0;
+    (*BDAddrs)[1] = new uint8_t[6];
+    (*IRKs)[1]    = new uint8_t[16];
+    memcpy((*IRKs)[1]   , device2IRK,16);
+    memcpy((*BDAddrs)[1], device2Mac, 6);
+
+
+    return 1;
+  });
+  // The LTK is the secret key which is used to encrypt bluetooth traffic
+  BLE.setGetLTK([](uint8_t* address, uint8_t* LTK){
+    // address is input
+    Serial.print("Received request for address: ");
+    btct.printBytes(address,6);
+
+    // Set these to the MAC and LTK of your devices after bonding.
+    uint8_t device1Mac[6]  = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    uint8_t device1LTK[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    uint8_t device2Mac[6]  = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    uint8_t device2LTK[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    
+
+    if(memcmp(device1Mac, address, 6) == 0) {
+      memcpy(LTK, device1LTK, 16);
+      return 1;
+    }else if(memcmp(device2Mac, address, 6) == 0) {
+      memcpy(LTK, device2LTK, 16);
+      return 1;
+    }
+    return 0;
+  });
+  BLE.setStoreIRK([](uint8_t* address, uint8_t* IRK){
+    Serial.print(F("New device with MAC : "));
+    btct.printBytes(address,6);
+    Serial.print(F("Need to store IRK   : "));
+    btct.printBytes(IRK,16);
+    return 1;
+  });
+  BLE.setStoreLTK([](uint8_t* address, uint8_t* LTK){
+    Serial.print(F("New device with MAC : "));
+    btct.printBytes(address,6);
+    Serial.print(F("Need to store LTK   : "));
+    btct.printBytes(LTK,16);
+    return 1;
+  });
+
+  while(1){
+    // begin initialization
+    if (!BLE.begin()) {
+      Serial.println("starting BLE failed!");
+      delay(200);
+      continue;
+    }
+    Serial.println("BT init");
+    delay(200);
+    
+    /* Set a local name for the BLE device
+       This name will appear in advertising packets
+       and can be used by remote devices to identify this BLE device
+       The name can be changed but maybe be truncated based on space left in advertisement packet
+    */
+
+    BLE.setDeviceName("Arduino");
+    BLE.setLocalName("BatteryMonitor");
+
+    BLE.setAdvertisedService(batteryService); // add the service UUID
+    batteryService.addCharacteristic(batteryLevelChar); // add the battery level characteristic
+    batteryService.addCharacteristic(stringcharacteristic);
+    batteryService.addCharacteristic(secretValue);
+
+    BLE.addService(batteryService);               // Add the battery service
+    batteryLevelChar.writeValue(oldBatteryLevel); // set initial value for this characteristic
+    char* stringCharValue = new char[32];
+    stringCharValue = "string";
+    stringcharacteristic.writeValue(stringCharValue);
+    secretValue.writeValue(0);
+    
+    delay(1000);
+
+    // prevent pairing until button is pressed (will show a pairing rejected message)
+    BLE.setPairable(false);
+  
+    /* Start advertising BLE.  It will start continuously transmitting BLE
+       advertising packets and will be visible to remote BLE central devices
+       until it receives a new connection */
+  
+    // start advertising
+    if(!BLE.advertise()){
+      Serial.println("failed to advertise bluetooth.");
+      BLE.stopAdvertise();
+      delay(500);
+    }else{
+      Serial.println("advertising...");
+      break;
+    }
+    BLE.end();
+    delay(100);
+  }
+}
+
+
+void loop() {
+  // wait for a BLE central
+  BLEDevice central = BLE.central();
+
+
+  // If button is pressed, allow pairing for 30 sec
+  if (!BLE.pairable() && digitalRead(PAIR_BUTTON) == LOW){
+    pairingStarted = millis();
+    BLE.setPairable(Pairable::ONCE);
+    Serial.println("Accepting pairing for 30s");
+  } else if (BLE.pairable() && millis() > pairingStarted + PAIR_INTERVAL){
+    BLE.setPairable(false);
+    Serial.println("No longer accepting pairing");
+  }
+  // Make LED blink while pairing is allowed
+  digitalWrite(PAIR_LED, (BLE.pairable() ? (millis()%400)<200 : BLE.paired()) ? PAIR_LED_ON : !PAIR_LED_ON); 
+
+
+  // if a central is connected to the peripheral:
+  if (central && central.connected()) {
+    if (!wasConnected){
+      wasConnected = true;
+      Serial.print("Connected to central: ");
+      // print the central's BT address:
+      Serial.println(central.address());
+    }
+
+    // check the battery level every 200ms
+    // while the central is connected:
+    long currentMillis = millis();
+    // if 200ms have passed, check the battery level:
+    if (currentMillis - previousMillis >= 1000) {
+      previousMillis = currentMillis;
+      updateBatteryLevel();
+      digitalWrite(CTRL_LED, secretValue.value()>0 ? HIGH : LOW);
+    }
+  } else if (wasConnected){
+    wasConnected = false;
+    Serial.print("Disconnected from central: ");
+    Serial.println(central.address());
+  }
+    
+}
+
+void updateBatteryLevel() {
+  /* Read the current voltage level on the A0 analog input pin.
+     This is used here to simulate the charge level of a battery.
+  */
+  int battery = analogRead(A0);
+  int batteryLevel = map(battery, 0, 1023, 0, 100);
+
+  if (batteryLevel != oldBatteryLevel) {      // if the battery level has changed
+    // Serial.print("Battery Level % is now: "); // print it
+    // Serial.println(batteryLevel);
+    batteryLevelChar.writeValue(batteryLevel);  // and update the battery level characteristic
+    oldBatteryLevel = batteryLevel;           // save the level for next comparison
+  }
+}
\ No newline at end of file
diff --git a/examples/Peripheral/LED/LED.ino b/examples/Peripheral/LED/LED.ino
index 1ba9c9a8..d92dfd6d 100644
--- a/examples/Peripheral/LED/LED.ino
+++ b/examples/Peripheral/LED/LED.ino
@@ -1,7 +1,7 @@
 /*
   LED
 
-  This example creates a BLE peripheral with service that contains a
+  This example creates a Bluetooth® Low Energy peripheral with service that contains a
   characteristic to control an LED.
 
   The circuit:
@@ -17,7 +17,7 @@
     - X-NUCLEO-IDB05A1
     - X-NUCLEO-BNRG2A1
 
-  You can use a generic BLE central app, like LightBlue (iOS and Android) or
+  You can use a generic Bluetooth® Low Energy central app, like LightBlue (iOS and Android) or
   nRF Connect (Android), to interact with the services and characteristics
   created in this sketch.
 
@@ -101,9 +101,9 @@ BLELocalDevice& BLE = BLEObj;
 // #endif
 #endif
 
-BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // BLE LED Service
+BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // Bluetooth® Low Energy LED Service
 
-// BLE LED Switch Characteristic - custom 128-bit UUID, read and writable by central
+// Bluetooth® Low Energy LED Switch Characteristic - custom 128-bit UUID, read and writable by central
 BLEByteCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);
 
 const int ledPin = LED_BUILTIN; // pin to use for the LED
@@ -117,7 +117,7 @@ void setup() {
 
   // begin initialization
   if (!BLE.begin()) {
-    Serial.println("starting BLE failed!");
+    Serial.println("starting Bluetooth® Low Energy module failed!");
 
     while (1);
   }
@@ -142,7 +142,7 @@ void setup() {
 }
 
 void loop() {
-  // listen for BLE peripherals to connect:
+  // listen for Bluetooth® Low Energy peripherals to connect:
   BLEDevice central = BLE.central();
 
   // if a central is connected to peripheral:
diff --git a/keywords.txt b/keywords.txt
index e13de56d..4f5bed56 100644
--- a/keywords.txt
+++ b/keywords.txt
@@ -79,9 +79,12 @@ setEventHandler	KEYWORD2
 setAdvertisingInterval	KEYWORD2
 setConnectionInterval	KEYWORD2
 setConnectable	KEYWORD2
+setPairable	KEYWORD2
 setTimeout	KEYWORD2
 debug	KEYWORD2
 noDebug	KEYWORD2
+pairable	KEYWORD2
+paired	KEYWORD2
 setTransport	KEYWORD2
 
 properties	KEYWORD2
diff --git a/src/BLECharacteristic.cpp b/src/BLECharacteristic.cpp
index 9a07cb9d..cb9783ce 100644
--- a/src/BLECharacteristic.cpp
+++ b/src/BLECharacteristic.cpp
@@ -47,13 +47,13 @@ BLECharacteristic::BLECharacteristic(BLERemoteCharacteristic* remote) :
   }
 }
 
-BLECharacteristic::BLECharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength) :
-  BLECharacteristic(new BLELocalCharacteristic(uuid, properties, valueSize, fixedLength))
+BLECharacteristic::BLECharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength) :
+  BLECharacteristic(new BLELocalCharacteristic(uuid, permissions, valueSize, fixedLength))
 {
 }
 
-BLECharacteristic::BLECharacteristic(const char* uuid, uint8_t properties, const char* value) :
-  BLECharacteristic(new BLELocalCharacteristic(uuid, properties, value))
+BLECharacteristic::BLECharacteristic(const char* uuid, uint16_t permissions, const char* value) :
+  BLECharacteristic(new BLELocalCharacteristic(uuid, permissions, value))
 {
 }
 
@@ -72,11 +72,11 @@ BLECharacteristic::BLECharacteristic(const BLECharacteristic& other)
 
 BLECharacteristic::~BLECharacteristic()
 {
-  if (_local && _local->release() <= 0) {
+  if (_local && _local->release() == 0) {
     delete _local;
   }
 
-  if (_remote && _remote->release() <= 0) {
+  if (_remote && _remote->release() == 0) {
     delete _remote;
   }
 }
diff --git a/src/BLECharacteristic.h b/src/BLECharacteristic.h
index 2ac30b05..da9721e0 100644
--- a/src/BLECharacteristic.h
+++ b/src/BLECharacteristic.h
@@ -45,8 +45,8 @@ class BLERemoteCharacteristic;
 class BLECharacteristic  {
 public:
   BLECharacteristic();
-  BLECharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength = false);
-  BLECharacteristic(const char* uuid, uint8_t properties, const char* value);
+  BLECharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength = false);
+  BLECharacteristic(const char* uuid, uint16_t permissions, const char* value);
   BLECharacteristic(const BLECharacteristic& other);
   virtual ~BLECharacteristic();
 
diff --git a/src/BLEDescriptor.cpp b/src/BLEDescriptor.cpp
index 7a6736b0..366b89aa 100644
--- a/src/BLEDescriptor.cpp
+++ b/src/BLEDescriptor.cpp
@@ -72,11 +72,11 @@ BLEDescriptor::BLEDescriptor(const BLEDescriptor& other)
 
 BLEDescriptor::~BLEDescriptor()
 {
-  if (_local && _local->release() <= 0) {
+  if (_local && _local->release() == 0) {
     delete _local;
   }
 
-  if (_remote && _remote->release() <= 0) {
+  if (_remote && _remote->release() == 0) {
     delete _remote;
   }
 }
diff --git a/src/BLEDevice.cpp b/src/BLEDevice.cpp
index fa806104..5f64c1d6 100644
--- a/src/BLEDevice.cpp
+++ b/src/BLEDevice.cpp
@@ -184,6 +184,70 @@ String BLEDevice::advertisedServiceUuid(int index) const
   return serviceUuid;
 }
 
+bool BLEDevice::hasAdvertisementData() const
+{
+  return (_eirDataLength > 0);
+}
+
+int BLEDevice::advertisementDataLength() const
+{
+  return _eirDataLength;
+}
+
+int BLEDevice::advertisementData(uint8_t value[], int length) const
+{
+  if (length > _eirDataLength) length = _eirDataLength;
+
+  if (length) {
+    memcpy(value, _eirData, length);
+  }
+
+  return length;
+}
+
+bool BLEDevice::hasManufacturerData() const
+{
+  return (manufacturerDataLength() > 0);
+}
+
+int BLEDevice::manufacturerDataLength() const
+{
+  int length = 0;
+
+  for (int i = 0; i < _eirDataLength;) {
+    int eirLength = _eirData[i++];
+    int eirType = _eirData[i++];
+
+    if (eirType == 0xFF) {
+      length = (eirLength - 1);
+      break;
+    }
+
+    i += (eirLength - 1);
+  }
+
+  return length;
+}
+
+int BLEDevice::manufacturerData(uint8_t value[], int length) const
+{
+  for (int i = 0; i < _eirDataLength;) {
+    int eirLength = _eirData[i++];
+    int eirType = _eirData[i++];
+
+    if (eirType == 0xFF) {
+      if (length > (eirLength - 1)) length = (eirLength - 1);
+
+      memcpy(value, &_eirData[i], length);
+      break;
+    }
+
+    i += (eirLength - 1);
+  }
+
+  return length;
+}
+
 int BLEDevice::rssi()
 {
   uint16_t handle = ATT.connectionHandle(_addressType, _address);
diff --git a/src/BLEDevice.h b/src/BLEDevice.h
index cbe79c71..bf710744 100644
--- a/src/BLEDevice.h
+++ b/src/BLEDevice.h
@@ -59,6 +59,14 @@ class BLEDevice {
   String advertisedServiceUuid() const;
   String advertisedServiceUuid(int index) const;
 
+  bool hasAdvertisementData() const;
+  int advertisementDataLength() const;
+  int advertisementData(uint8_t value[], int length) const;
+
+  bool hasManufacturerData() const;
+  int manufacturerDataLength() const;
+  int manufacturerData(uint8_t value[], int length) const;
+
   virtual int rssi();
 
   bool connect();
diff --git a/src/BLEProperty.h b/src/BLEProperty.h
index 6cdd888f..b9238ba9 100644
--- a/src/BLEProperty.h
+++ b/src/BLEProperty.h
@@ -17,6 +17,8 @@
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
 
+// #include <stdint.h>
+
 #ifndef _BLE_PROPERTY_H_
 #define _BLE_PROPERTY_H_
 
@@ -26,7 +28,52 @@ enum BLEProperty {
   BLEWriteWithoutResponse = 0x04,
   BLEWrite                = 0x08,
   BLENotify               = 0x10,
-  BLEIndicate             = 0x20
+  BLEIndicate             = 0x20,
+  BLEAuthSignedWrite      = 1 << 6,
+  BLEExtProp              = 1 << 7,
+};
+
+enum BLEPermission {
+  BLEEncryption         = 1 << 9,
+  BLEAuthentication     = 1 << 10,
+  BLEAuthorization      = 1 << 11,
+  // BLEWriteEncryption        = 1 << 11,
+  // BLEWriteAuthentication    = 1 << 12,
+  // BLEWriteAuthorization     = 1 << 13,
+};
+
+#define    ESP_GATT_CHAR_PROP_BIT_BROADCAST    (1 << 0)       /* 0x01 */    /* relate to BTA_GATT_CHAR_PROP_BIT_BROADCAST in bta/bta_gatt_api.h */
+#define    ESP_GATT_CHAR_PROP_BIT_READ         (1 << 1)       /* 0x02 */    /* relate to BTA_GATT_CHAR_PROP_BIT_READ in bta/bta_gatt_api.h */
+#define    ESP_GATT_CHAR_PROP_BIT_WRITE_NR     (1 << 2)       /* 0x04 */    /* relate to BTA_GATT_CHAR_PROP_BIT_WRITE_NR in bta/bta_gatt_api.h */
+#define    ESP_GATT_CHAR_PROP_BIT_WRITE        (1 << 3)       /* 0x08 */    /* relate to BTA_GATT_CHAR_PROP_BIT_WRITE in bta/bta_gatt_api.h */
+#define    ESP_GATT_CHAR_PROP_BIT_NOTIFY       (1 << 4)       /* 0x10 */    /* relate to BTA_GATT_CHAR_PROP_BIT_NOTIFY in bta/bta_gatt_api.h */
+#define    ESP_GATT_CHAR_PROP_BIT_INDICATE     (1 << 5)       /* 0x20 */    /* relate to BTA_GATT_CHAR_PROP_BIT_INDICATE in bta/bta_gatt_api.h */
+#define    ESP_GATT_CHAR_PROP_BIT_AUTH         (1 << 6)       /* 0x40 */    /* relate to BTA_GATT_CHAR_PROP_BIT_AUTH in bta/bta_gatt_api.h */
+#define    ESP_GATT_CHAR_PROP_BIT_EXT_PROP     (1 << 7)       /* 0x80 */    /* relate to BTA_GATT_CHAR_PROP_BIT_EXT_PROP in bta/bta_gatt_api.h */
+
+#define    ESP_GATT_PERM_READ                  (1 << 0)   /* bit 0 -  0x0001 */    /* relate to BTA_GATT_PERM_READ in bta/bta_gatt_api.h */
+#define    ESP_GATT_PERM_READ_ENCRYPTED        (1 << 1)   /* bit 1 -  0x0002 */    /* relate to BTA_GATT_PERM_READ_ENCRYPTED in bta/bta_gatt_api.h */
+#define    ESP_GATT_PERM_READ_ENC_MITM         (1 << 2)   /* bit 2 -  0x0004 */    /* relate to BTA_GATT_PERM_READ_ENC_MITM in bta/bta_gatt_api.h */
+#define    ESP_GATT_PERM_WRITE                 (1 << 4)   /* bit 4 -  0x0010 */    /* relate to BTA_GATT_PERM_WRITE in bta/bta_gatt_api.h */
+#define    ESP_GATT_PERM_WRITE_ENCRYPTED       (1 << 5)   /* bit 5 -  0x0020 */    /* relate to BTA_GATT_PERM_WRITE_ENCRYPTED in bta/bta_gatt_api.h */
+#define    ESP_GATT_PERM_WRITE_ENC_MITM        (1 << 6)   /* bit 6 -  0x0040 */    /* relate to BTA_GATT_PERM_WRITE_ENC_MITM in bta/bta_gatt_api.h */
+#define    ESP_GATT_PERM_WRITE_SIGNED          (1 << 7)   /* bit 7 -  0x0080 */    /* relate to BTA_GATT_PERM_WRITE_SIGNED in bta/bta_gatt_api.h */
+#define    ESP_GATT_PERM_WRITE_SIGNED_MITM     (1 << 8)   /* bit 8 -  0x0100 */    /* relate to BTA_GATT_PERM_WRITE_SIGNED_MITM in bta/bta_gatt_api.h */
+#define    ESP_GATT_PERM_READ_AUTHORIZATION    (1 << 9)   /* bit 9 -  0x0200 */
+#define    ESP_GATT_PERM_WRITE_AUTHORIZATION   (1 << 10)  /* bit 10 - 0x0400 */
+
+enum BLE_GATT_PERM_ {
+  BLE_GATT_READ       = 1 << 0,
+  READ_ENCRYPTED      = 1 << 1,
+  READ_ENC_MITM       = 1 << 2,
+  BLE_GATT_WRITE      = 1 << 4,
+  WRITE_ENCRYPTED     = 1 << 5,
+  WRITE_ENC_MITM      = 1 << 6,
+  WRITE_SIGNED        = 1 << 7,
+  WRITE_SIGNED_MITM   = 1 << 8,
+  READ_AUTHORIZATION  = 1 << 9,
+  WRITE_AUTHORIZATION = 1 << 10,
 };
 
+
 #endif
diff --git a/src/BLEService.cpp b/src/BLEService.cpp
index 7b5df148..ed54f9ff 100644
--- a/src/BLEService.cpp
+++ b/src/BLEService.cpp
@@ -63,13 +63,20 @@ BLEService::BLEService(const BLEService& other)
   }
 }
 
+void BLEService::clear()
+{
+  if (_local) {
+    _local->clear();
+  }
+}
+
 BLEService::~BLEService()
 {
-  if (_local && _local->release() <= 0) {
+  if (_local && _local->release() == 0) {
     delete _local;
   }
 
-  if (_remote && _remote->release() <= 0) {
+  if (_remote && _remote->release() == 0) {
     delete _remote;
   }
 }
diff --git a/src/BLEService.h b/src/BLEService.h
index a8d8b010..5b2440d8 100644
--- a/src/BLEService.h
+++ b/src/BLEService.h
@@ -33,6 +33,7 @@ class BLEService {
   virtual ~BLEService();
 
   const char* uuid() const;
+  void clear();
 
   void addCharacteristic(BLECharacteristic& characteristic);
 
diff --git a/src/BLETypedCharacteristic.h b/src/BLETypedCharacteristic.h
index d6e6e4a1..7777d360 100644
--- a/src/BLETypedCharacteristic.h
+++ b/src/BLETypedCharacteristic.h
@@ -25,7 +25,7 @@
 template<typename T> class BLETypedCharacteristic : public BLECharacteristic
 {
 public:
-  BLETypedCharacteristic(const char* uuid, unsigned char properties);
+  BLETypedCharacteristic(const char* uuid, unsigned int permissions);
 
   int writeValue(T value);
   int setValue(T value) { return writeValue(value); }
@@ -43,8 +43,8 @@ template<typename T> class BLETypedCharacteristic : public BLECharacteristic
   T byteSwap(T value);
 };
 
-template<typename T> BLETypedCharacteristic<T>::BLETypedCharacteristic(const char* uuid, unsigned char properties) :
-  BLECharacteristic(uuid, properties, sizeof(T), true)
+template<typename T> BLETypedCharacteristic<T>::BLETypedCharacteristic(const char* uuid, unsigned int permissions) :
+  BLECharacteristic(uuid, permissions, sizeof(T), true)
 {
   T value;
   memset(&value, 0x00, sizeof(value));
diff --git a/src/BLETypedCharacteristics.cpp b/src/BLETypedCharacteristics.cpp
index 976f6159..800574eb 100644
--- a/src/BLETypedCharacteristics.cpp
+++ b/src/BLETypedCharacteristics.cpp
@@ -21,72 +21,72 @@
 
 #include "BLETypedCharacteristics.h"
 
-BLEBoolCharacteristic::BLEBoolCharacteristic(const char* uuid, unsigned char properties) :
+BLEBoolCharacteristic::BLEBoolCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<bool>(uuid, properties)
 {
 }
 
-BLEBooleanCharacteristic::BLEBooleanCharacteristic(const char* uuid, unsigned char properties) :
+BLEBooleanCharacteristic::BLEBooleanCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<bool>(uuid, properties)
 {
 }
 
-BLECharCharacteristic::BLECharCharacteristic(const char* uuid, unsigned char properties) :
+BLECharCharacteristic::BLECharCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<char>(uuid, properties)
 {
 }
 
-BLEUnsignedCharCharacteristic::BLEUnsignedCharCharacteristic(const char* uuid, unsigned char properties) :
+BLEUnsignedCharCharacteristic::BLEUnsignedCharCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<unsigned char>(uuid, properties)
 {
 }
 
-BLEByteCharacteristic::BLEByteCharacteristic(const char* uuid, unsigned char properties) :
+BLEByteCharacteristic::BLEByteCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<byte>(uuid, properties)
 {
 }
 
-BLEShortCharacteristic::BLEShortCharacteristic(const char* uuid, unsigned char properties) :
+BLEShortCharacteristic::BLEShortCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<short>(uuid, properties)
 {
 }
 
-BLEUnsignedShortCharacteristic::BLEUnsignedShortCharacteristic(const char* uuid, unsigned char properties) :
+BLEUnsignedShortCharacteristic::BLEUnsignedShortCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<unsigned short>(uuid, properties)
 {
 }
 
-BLEWordCharacteristic::BLEWordCharacteristic(const char* uuid, unsigned char properties) :
+BLEWordCharacteristic::BLEWordCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<word>(uuid, properties)
 {
 }
 
-BLEIntCharacteristic::BLEIntCharacteristic(const char* uuid, unsigned char properties) :
+BLEIntCharacteristic::BLEIntCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<int>(uuid, properties) 
 {
 }
 
-BLEUnsignedIntCharacteristic::BLEUnsignedIntCharacteristic(const char* uuid, unsigned char properties) :
+BLEUnsignedIntCharacteristic::BLEUnsignedIntCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<unsigned int>(uuid, properties)
 {
 }
 
-BLELongCharacteristic::BLELongCharacteristic(const char* uuid, unsigned char properties) :
+BLELongCharacteristic::BLELongCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<long>(uuid, properties)
 {
 }
 
-BLEUnsignedLongCharacteristic::BLEUnsignedLongCharacteristic(const char* uuid, unsigned char properties) :
+BLEUnsignedLongCharacteristic::BLEUnsignedLongCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<unsigned long>(uuid, properties)
 {
 }
 
-BLEFloatCharacteristic::BLEFloatCharacteristic(const char* uuid, unsigned char properties) :
+BLEFloatCharacteristic::BLEFloatCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<float>(uuid, properties)
 {
 }
 
-BLEDoubleCharacteristic::BLEDoubleCharacteristic(const char* uuid, unsigned char properties) :
+BLEDoubleCharacteristic::BLEDoubleCharacteristic(const char* uuid, unsigned int properties) :
   BLETypedCharacteristic<double>(uuid, properties)
 {
 }
diff --git a/src/BLETypedCharacteristics.h b/src/BLETypedCharacteristics.h
index 8bd98c78..8e9a08c5 100644
--- a/src/BLETypedCharacteristics.h
+++ b/src/BLETypedCharacteristics.h
@@ -24,72 +24,72 @@
 
 class BLEBoolCharacteristic : public BLETypedCharacteristic<bool> {
 public:
-  BLEBoolCharacteristic(const char* uuid, unsigned char properties);
+  BLEBoolCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLEBooleanCharacteristic : public BLETypedCharacteristic<bool> {
 public:
-  BLEBooleanCharacteristic(const char* uuid, unsigned char properties);
+  BLEBooleanCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLECharCharacteristic : public BLETypedCharacteristic<char> {
 public:
-  BLECharCharacteristic(const char* uuid, unsigned char properties);
+  BLECharCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLEUnsignedCharCharacteristic : public BLETypedCharacteristic<unsigned char> {
 public:
-  BLEUnsignedCharCharacteristic(const char* uuid, unsigned char properties);
+  BLEUnsignedCharCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLEByteCharacteristic : public BLETypedCharacteristic<byte> {
 public:
-  BLEByteCharacteristic(const char* uuid, unsigned char properties);
+  BLEByteCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLEShortCharacteristic : public BLETypedCharacteristic<short> {
 public:
-  BLEShortCharacteristic(const char* uuid, unsigned char properties);
+  BLEShortCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLEUnsignedShortCharacteristic : public BLETypedCharacteristic<unsigned short> {
 public:
-  BLEUnsignedShortCharacteristic(const char* uuid, unsigned char properties);
+  BLEUnsignedShortCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLEWordCharacteristic : public BLETypedCharacteristic<word> {
 public:
-  BLEWordCharacteristic(const char* uuid, unsigned char properties);
+  BLEWordCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLEIntCharacteristic : public BLETypedCharacteristic<int> {
 public:
-  BLEIntCharacteristic(const char* uuid, unsigned char properties);
+  BLEIntCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLEUnsignedIntCharacteristic : public BLETypedCharacteristic<unsigned int> {
 public:
-  BLEUnsignedIntCharacteristic(const char* uuid, unsigned char properties);
+  BLEUnsignedIntCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLELongCharacteristic : public BLETypedCharacteristic<long> {
 public:
-  BLELongCharacteristic(const char* uuid, unsigned char properties);
+  BLELongCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLEUnsignedLongCharacteristic : public BLETypedCharacteristic<unsigned long> {
 public:
-  BLEUnsignedLongCharacteristic(const char* uuid, unsigned char properties);
+  BLEUnsignedLongCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLEFloatCharacteristic : public BLETypedCharacteristic<float> {
 public:
-  BLEFloatCharacteristic(const char* uuid, unsigned char properties);
+  BLEFloatCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 class BLEDoubleCharacteristic : public BLETypedCharacteristic<double> {
 public:
-  BLEDoubleCharacteristic(const char* uuid, unsigned char properties);
+  BLEDoubleCharacteristic(const char* uuid, unsigned int permissions);
 };
 
 #endif
diff --git a/src/STM32duinoBLE.h b/src/STM32duinoBLE.h
index 4bf51468..4ad826bc 100644
--- a/src/STM32duinoBLE.h
+++ b/src/STM32duinoBLE.h
@@ -24,6 +24,7 @@
 #include "BLEProperty.h"
 #include "BLEStringCharacteristic.h"
 #include "BLETypedCharacteristics.h"
+#include "utility/btct.h"
 
 #if defined(STM32WBxx)
 #include "utility/HCISharedMemTransport.h"
diff --git a/src/local/BLELocalAttribute.cpp b/src/local/BLELocalAttribute.cpp
index ce3010a0..9929fa41 100644
--- a/src/local/BLELocalAttribute.cpp
+++ b/src/local/BLELocalAttribute.cpp
@@ -56,6 +56,11 @@ int BLELocalAttribute::retain()
   return _refCount;
 }
 
+bool BLELocalAttribute::active()
+{
+  return _refCount > 0;
+}
+
 int BLELocalAttribute::release()
 {
   _refCount--;
diff --git a/src/local/BLELocalAttribute.h b/src/local/BLELocalAttribute.h
index 2af948c3..53a0abfe 100644
--- a/src/local/BLELocalAttribute.h
+++ b/src/local/BLELocalAttribute.h
@@ -44,6 +44,7 @@ class BLELocalAttribute
 
   int retain();
   int release();
+  bool active();
 
 protected:
   friend class ATTClass;
diff --git a/src/local/BLELocalCharacteristic.cpp b/src/local/BLELocalCharacteristic.cpp
index 333d00b2..207425bd 100644
--- a/src/local/BLELocalCharacteristic.cpp
+++ b/src/local/BLELocalCharacteristic.cpp
@@ -29,9 +29,10 @@
 
 #include "BLELocalCharacteristic.h"
 
-BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength) :
+BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength) :
   BLELocalAttribute(uuid),
-  _properties(properties),
+  _properties((uint8_t)(permissions&0x000FF)),
+  _permissions((uint8_t)((permissions&0xFF00)>>8)),
   _valueSize(min(valueSize, 512)),
   _valueLength(0),
   _fixedLength(fixedLength),
@@ -42,27 +43,27 @@ BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint8_t propert
 {
   memset(_eventHandlers, 0x00, sizeof(_eventHandlers));
 
-  if (properties & (BLENotify | BLEIndicate)) {
+  if (permissions & (BLENotify | BLEIndicate)) {
     BLELocalDescriptor* cccd = new BLELocalDescriptor("2902", (uint8_t*)&_cccdValue, sizeof(_cccdValue));
   
+    cccd->retain();
     _descriptors.add(cccd);
   }
 
   _value = (uint8_t*)malloc(valueSize);
 }
 
-BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint8_t properties, const char* value) :
-  BLELocalCharacteristic(uuid, properties, strlen(value))
+BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint16_t permissions, const char* value) :
+  BLELocalCharacteristic(uuid, permissions, strlen(value))
 {
   writeValue(value);
 }
-
 BLELocalCharacteristic::~BLELocalCharacteristic()
 {
   for (unsigned int i = 0; i < descriptorCount(); i++) {
     BLELocalDescriptor* d = descriptor(i);
 
-    if (d->release() <= 0) {
+    if (d->release() == 0) {
       delete d;
     }
   }
@@ -84,6 +85,10 @@ uint8_t BLELocalCharacteristic::properties() const
   return _properties;
 }
 
+uint8_t BLELocalCharacteristic::permissions() const {
+  return _permissions;
+}
+
 int BLELocalCharacteristic::valueSize() const
 {
   return _valueSize;
diff --git a/src/local/BLELocalCharacteristic.h b/src/local/BLELocalCharacteristic.h
index ee42390a..331cdd5c 100644
--- a/src/local/BLELocalCharacteristic.h
+++ b/src/local/BLELocalCharacteristic.h
@@ -33,13 +33,14 @@ class BLELocalDescriptor;
 
 class BLELocalCharacteristic : public BLELocalAttribute {
 public:
-  BLELocalCharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength = false);
-  BLELocalCharacteristic(const char* uuid, uint8_t properties, const char* value);
+  BLELocalCharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength = false);
+  BLELocalCharacteristic(const char* uuid, uint16_t permissions, const char* value);
   virtual ~BLELocalCharacteristic();
 
   virtual enum BLEAttributeType type() const;
 
   uint8_t properties() const;
+  uint8_t permissions() const;
 
   int valueSize() const;
   const uint8_t* value() const;
@@ -75,6 +76,7 @@ class BLELocalCharacteristic : public BLELocalAttribute {
 
 private:
   uint8_t  _properties;
+  uint8_t  _permissions;
   int      _valueSize;
   uint8_t* _value;
   uint16_t  _valueLength;
diff --git a/src/local/BLELocalDevice.cpp b/src/local/BLELocalDevice.cpp
index 60519dc6..e1a17143 100644
--- a/src/local/BLELocalDevice.cpp
+++ b/src/local/BLELocalDevice.cpp
@@ -68,11 +68,19 @@ int BLELocalDevice::begin()
   randomAddress [3] = randomNumber[3];
   randomAddress [4] = randomNumber[4];
   randomAddress [5] = randomNumber[5];
-
-  if (HCI.leSetRandomAddress((uint8_t*)randomNumber) != 0) {
+  // @note Set Random address only when type is STATIC_RANDOM_ADDR
+  if (HCI.leSetRandomAddress((uint8_t*)randomNumber) != 0 && _ownBdaddrType == STATIC_RANDOM_ADDR) {
     end();
     return 0;
   }
+  // @note Save address to HCI.localaddress variable, which is used to encryption in pairing
+  if(_ownBdaddrType == PUBLIC_ADDR){
+    HCI.readBdAddr();
+  }else{
+    for(int k=0; k<6; k++){
+      HCI.localAddr[5-k] = randomAddress[k];
+    }
+  }
 
   uint8_t hciVer;
   uint16_t hciRev;
@@ -85,7 +93,11 @@ int BLELocalDevice::begin()
     return 0;
   }
 
-  if (HCI.setEventMask(0x3FFFFFFFFFFFFFFF) != 0) {
+  if (HCI.setEventMask(0x2000F0FFFFFFF0FF) != 0) {
+    end();
+    return 0;
+  }
+  if (HCI.setLeEventMask(0x0000000000000193) != 0) {
     end();
     return 0;
   }
@@ -98,6 +110,59 @@ int BLELocalDevice::begin()
     return 0;
   }
 
+  /// The HCI should allow automatic address resolution.
+
+  // // If we have callbacks to remember bonded devices:
+  // if(HCI._getIRKs!=0){
+  //   uint8_t nIRKs = 0;
+  //   uint8_t** BADDR_Type = new uint8_t*;
+  //   uint8_t*** BADDRs = new uint8_t**;
+  //   uint8_t*** IRKs = new uint8_t**;
+  //   uint8_t* memcheck;
+
+
+  //   if(!HCI._getIRKs(&nIRKs, BADDR_Type, BADDRs, IRKs)){
+  //     Serial.println("error");
+  //   }
+  //   for(int i=0; i<nIRKs; i++){
+  //     Serial.print("Baddr type: ");
+  //     Serial.println((*BADDR_Type)[i]);
+  //     Serial.print("BADDR:");
+  //     for(int k=0; k<6; k++){
+  //       Serial.print(", 0x");
+  //       Serial.print((*BADDRs)[i][k],HEX);
+  //     }
+  //     Serial.println();
+  //     Serial.print("IRK:");
+  //     for(int k=0; k<16; k++){
+  //       Serial.print(", 0x");
+  //       Serial.print((*IRKs)[i][k],HEX);
+  //     }
+  //     Serial.println();
+
+  //     // save this 
+  //     uint8_t zeros[16];
+  //     for(int k=0; k<16; k++) zeros[15-k] = 0;
+      
+  //     // HCI.leAddResolvingAddress((*BADDR_Type)[i],(*BADDRs)[i],(*IRKs)[i], zeros);
+
+  //     delete[] (*BADDRs)[i];
+  //     delete[] (*IRKs)[i];
+  //   }
+  //   delete[] (*BADDR_Type);
+  //   delete BADDR_Type;
+  //   delete[] (*BADDRs);
+  //   delete BADDRs;
+  //   delete[] (*IRKs);
+  //   delete IRKs;
+    
+  //   memcheck = new uint8_t[1];
+  //   Serial.print("nIRK location: 0x");
+  //   Serial.println((int)memcheck,HEX);
+  //   delete[] memcheck;
+
+  // }
+
   GATT.begin();
 
   GAP.setOwnBdaddrType(_ownBdaddrType);
@@ -141,6 +206,16 @@ bool BLELocalDevice::connected() const
   return ATT.connected();
 }
 
+/*
+ * Whether there is at least one paired device
+ */
+bool BLELocalDevice::paired()
+{
+  HCI.poll();
+
+  return ATT.paired();
+}
+
 bool BLELocalDevice::disconnect()
 {
   return ATT.disconnect();
@@ -149,7 +224,13 @@ bool BLELocalDevice::disconnect()
 String BLELocalDevice::address() const
 {
   uint8_t addr[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-  HCI.readBdAddr(addr);
+  // @note return correct device address when is set to STATIC RANDOM (set by HCI)
+  if(_ownBdaddrType==PUBLIC_ADDR)
+    HCI.readBdAddr(addr);
+  else
+    for(int k=0; k<6; k++){
+        addr[k]=HCI.localAddr[5-k];
+    }
 
   char result[18];
   sprintf(result, "%02x:%02x:%02x:%02x:%02x:%02x", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
@@ -322,6 +403,42 @@ void BLELocalDevice::setTimeout(unsigned long timeout)
   ATT.setTimeout(timeout);
 }
 
+/*
+ * Control whether pairing is allowed or rejected
+ * Use true/false or the Pairable enum
+ */
+void BLELocalDevice::setPairable(uint8_t pairable)
+{
+  L2CAPSignaling.setPairingEnabled(pairable);
+}
+
+/*
+ * Whether pairing is currently allowed
+ */
+bool BLELocalDevice::pairable()
+{
+  return L2CAPSignaling.isPairingEnabled();
+}
+
+void BLELocalDevice::setGetIRKs(int (*getIRKs)(uint8_t* nIRKs, uint8_t** BADDR_type, uint8_t*** BADDRs, uint8_t*** IRKs)){
+  HCI._getIRKs = getIRKs;
+}
+void BLELocalDevice::setGetLTK(int (*getLTK)(uint8_t* BADDR, uint8_t* LTK)){
+  HCI._getLTK = getLTK;
+}
+void BLELocalDevice::setStoreLTK(int (*storeLTK)(uint8_t*, uint8_t*)){
+  HCI._storeLTK = storeLTK;
+}
+void BLELocalDevice::setStoreIRK(int (*storeIRK)(uint8_t*, uint8_t*)){
+  HCI._storeIRK = storeIRK;
+}
+void BLELocalDevice::setDisplayCode(void (*displayCode)(uint32_t confirmationCode)){
+  HCI._displayCode = displayCode;
+}
+void BLELocalDevice::setBinaryConfirmPairing(bool (*binaryConfirmPairing)()){
+  HCI._binaryConfirmPairing = binaryConfirmPairing;
+}
+
 void BLELocalDevice::debug(Stream& stream)
 {
   HCI.debug(stream);
diff --git a/src/local/BLELocalDevice.h b/src/local/BLELocalDevice.h
index f0cc345c..dd114eb4 100644
--- a/src/local/BLELocalDevice.h
+++ b/src/local/BLELocalDevice.h
@@ -25,6 +25,11 @@
 #include "BLEService.h"
 #include "BLEAdvertisingData.h"
 
+enum Pairable {
+  NO = 0,
+  YES = 1,
+  ONCE = 2,
+};
 #define PUBLIC_ADDR                 (0)
 #define STATIC_RANDOM_ADDR          (1)
 #define RESOLVABLE_PRIVATE_ADDR     (2)
@@ -89,6 +94,28 @@ class BLELocalDevice {
   virtual void debug(Stream& stream);
   virtual void noDebug();
   
+  virtual void setPairable(uint8_t pairable);
+  virtual bool pairable();
+  virtual bool paired();
+
+  // address - The mac to store
+  // IRK - The IRK to store with this mac
+  virtual void setStoreIRK(int (*storeIRK)(uint8_t* address, uint8_t* IRK));
+  // nIRKs      - the number of IRKs being provided.
+  // BDAddrType - an array containing the type of each address (0 public, 1 static random)
+  // BDAddrs    - an array containing the list of addresses
+  virtual void setGetIRKs(int (*getIRKs)(uint8_t* nIRKs, uint8_t** BDAddrType, uint8_t*** BDAddrs, uint8_t*** IRKs));
+  // address - the address to store [6 bytes]
+  // LTK - the LTK to store with this mac [16 bytes]
+  virtual void setStoreLTK(int (*storeLTK)(uint8_t* address, uint8_t* LTK));
+  // address - The mac address needing its LTK
+  // LTK - 16 octet LTK for the mac address
+  virtual void setGetLTK(int (*getLTK)(uint8_t* address, uint8_t* LTK));
+
+  virtual void setDisplayCode(void (*displayCode)(uint32_t confirmationCode));
+  virtual void setBinaryConfirmPairing(bool (*binaryConfirmPairing)());
+  uint8_t BDaddress[6];
+  
 protected:
   virtual BLEAdvertisingData& getAdvertisingData();
   virtual BLEAdvertisingData& getScanResponseData();
diff --git a/src/local/BLELocalService.cpp b/src/local/BLELocalService.cpp
index 442c5422..7fda3cb4 100644
--- a/src/local/BLELocalService.cpp
+++ b/src/local/BLELocalService.cpp
@@ -28,17 +28,22 @@ BLELocalService::BLELocalService(const char* uuid) :
 {
 }
 
+void BLELocalService::clear() {
+  _characteristics.clear();
+  _startHandle = 0;
+  _endHandle = 0;
+}
+
 BLELocalService::~BLELocalService()
 {
   for (unsigned int i = 0; i < characteristicCount(); i++) {
     BLELocalCharacteristic* c = characteristic(i);
 
-    if (c->release() <= 0) {
+    if (c->release() == 0) {
       delete c;
     }
   }
-
-  _characteristics.clear();
+  clear();
 }
 
 enum BLEAttributeType BLELocalService::type() const
diff --git a/src/local/BLELocalService.h b/src/local/BLELocalService.h
index e0179a93..f17c610c 100644
--- a/src/local/BLELocalService.h
+++ b/src/local/BLELocalService.h
@@ -36,6 +36,7 @@ class BLELocalService : public BLELocalAttribute {
   virtual enum BLEAttributeType type() const;
 
   void addCharacteristic(BLECharacteristic& characteristic);
+  void clear();
 
 protected:
   friend class ATTClass;
diff --git a/src/remote/BLERemoteCharacteristic.cpp b/src/remote/BLERemoteCharacteristic.cpp
index bbd98ddf..454438b6 100644
--- a/src/remote/BLERemoteCharacteristic.cpp
+++ b/src/remote/BLERemoteCharacteristic.cpp
@@ -24,11 +24,12 @@
 #include "BLERemoteCharacteristic.h"
 
 BLERemoteCharacteristic::BLERemoteCharacteristic(const uint8_t uuid[], uint8_t uuidLen, uint16_t connectionHandle,
-                                                  uint16_t startHandle, uint8_t properties, uint16_t valueHandle) :
+                                                  uint16_t startHandle, uint16_t permissions, uint16_t valueHandle) :
   BLERemoteAttribute(uuid, uuidLen),
   _connectionHandle(connectionHandle),
   _startHandle(startHandle),
-  _properties(properties),
+  _properties((uint8_t)(permissions & 0x00FF)),
+  _permissions((uint8_t)((permissions & 0xFF00)>>8)),
   _valueHandle(valueHandle),
   _value(NULL),
   _valueLength(0),
@@ -43,7 +44,7 @@ BLERemoteCharacteristic::~BLERemoteCharacteristic()
   for (unsigned int i = 0; i < descriptorCount(); i++) {
     BLERemoteDescriptor* d = descriptor(i);
 
-    if (d->release() <= 0) {
+    if (d->release() == 0) {
       delete d;
     }
   }
diff --git a/src/remote/BLERemoteCharacteristic.h b/src/remote/BLERemoteCharacteristic.h
index d0ac09bc..b53ab031 100644
--- a/src/remote/BLERemoteCharacteristic.h
+++ b/src/remote/BLERemoteCharacteristic.h
@@ -29,10 +29,11 @@
 
 class BLERemoteCharacteristic : public BLERemoteAttribute {
 public:
-  BLERemoteCharacteristic(const uint8_t uuid[], uint8_t uuidLen, uint16_t connectionHandle, uint16_t startHandle, uint8_t properties, uint16_t valueHandle);
+  BLERemoteCharacteristic(const uint8_t uuid[], uint8_t uuidLen, uint16_t connectionHandle, uint16_t startHandle, uint16_t permissions, uint16_t valueHandle);
   virtual ~BLERemoteCharacteristic();
 
   uint8_t properties() const;
+  uint8_t permissions() const;
 
   const uint8_t* value() const;
   int valueLength() const;
@@ -66,6 +67,7 @@ class BLERemoteCharacteristic : public BLERemoteAttribute {
   uint16_t _connectionHandle;
   uint16_t _startHandle;
   uint8_t _properties;
+  uint8_t _permissions;
   uint16_t _valueHandle;
 
   uint8_t* _value;
diff --git a/src/remote/BLERemoteDevice.cpp b/src/remote/BLERemoteDevice.cpp
index 5a49f26f..1a4a67ab 100644
--- a/src/remote/BLERemoteDevice.cpp
+++ b/src/remote/BLERemoteDevice.cpp
@@ -50,7 +50,7 @@ void BLERemoteDevice::clearServices()
   for (unsigned int i = 0; i < serviceCount(); i++) {
     BLERemoteService* s = service(i);
 
-    if (s->release() <= 0) {
+    if (s->release() == 0) {
       delete s;
     }
   }
diff --git a/src/remote/BLERemoteService.cpp b/src/remote/BLERemoteService.cpp
index fd5c0ba6..f7461290 100644
--- a/src/remote/BLERemoteService.cpp
+++ b/src/remote/BLERemoteService.cpp
@@ -31,7 +31,7 @@ BLERemoteService::~BLERemoteService()
   for (unsigned int i = 0; i < characteristicCount(); i++) {
     BLERemoteCharacteristic* c = characteristic(i);
 
-    if (c->release() <= 0) {
+    if (c->release() == 0) {
       delete c;
     }
   }
diff --git a/src/utility/ATT.cpp b/src/utility/ATT.cpp
index 6c502068..811f46c8 100644
--- a/src/utility/ATT.cpp
+++ b/src/utility/ATT.cpp
@@ -81,6 +81,8 @@
 #define ATT_ECODE_UNSUPP_GRP_TYPE      0x10
 #define ATT_ECODE_INSUFF_RESOURCES     0x11
 
+// #define _BLE_TRACE_
+
 ATTClass::ATTClass() :
   _maxMtu(23),
   _timeout(5000),
@@ -95,6 +97,7 @@ ATTClass::ATTClass() :
     memset(_peers[i].address, 0x00, sizeof(_peers[i].address));
     _peers[i].mtu = 23;
     _peers[i].device = NULL;
+    _peers[i].encryption = 0x0;
   }
 
   memset(_eventHandlers, 0x00, sizeof(_eventHandlers));
@@ -252,6 +255,18 @@ void ATTClass::addConnection(uint16_t handle, uint8_t role, uint8_t peerBdaddrTy
   _peers[peerIndex].mtu = 23;
   _peers[peerIndex].addressType = peerBdaddrType;
   memcpy(_peers[peerIndex].address, peerBdaddr, sizeof(_peers[peerIndex].address));
+  uint8_t BDADDr[6];
+  for(int i=0; i<6; i++) BDADDr[5-i] = peerBdaddr[i];
+  if(HCI.tryResolveAddress(BDADDr,_peers[peerIndex].resolvedAddress)){
+#ifdef _BLE_TRACE_
+    Serial.println("Found match.");
+#endif
+  }else{
+#ifdef _BLE_TRACE_
+    Serial.println("No matching MAC");
+#endif
+    memset(&_peers[peerIndex].resolvedAddress, 0, 6);
+  }
 
   if (_eventHandlers[BLEConnected]) {
     _eventHandlers[BLEConnected](BLEDevice(peerBdaddrType, peerBdaddr));
@@ -267,12 +282,24 @@ void ATTClass::handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[
 
   uint16_t mtu = this->mtu(connectionHandle);
 
+#ifdef _BLE_TRACE_
+  Serial.print("data opcode: 0x");
+  Serial.println(opcode, HEX);
+#endif
   switch (opcode) {
     case ATT_OP_ERROR:
+#ifdef _BLE_TRACE_
+      Serial.println("[Info] data error");
+      // Serial.print("Error: ");
+      // btct.printBytes(data, dlen);
+#endif
       error(connectionHandle, dlen, data);
       break;
 
     case ATT_OP_MTU_REQ:
+#ifdef _BLE_TRACE_
+      Serial.println("MTU");
+#endif
       mtuReq(connectionHandle, dlen, data);
       break;
 
@@ -281,6 +308,9 @@ void ATTClass::handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[
       break;
 
     case ATT_OP_FIND_INFO_REQ:
+#ifdef _BLE_TRACE_
+      Serial.println("Find info");
+#endif
       findInfoReq(connectionHandle, mtu, dlen, data);
       break;
 
@@ -293,6 +323,9 @@ void ATTClass::handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[
       break;
 
     case ATT_OP_READ_BY_TYPE_REQ:
+#ifdef _BLE_TRACE_
+      Serial.println("By type");
+#endif
       readByTypeReq(connectionHandle, mtu, dlen, data);
       break;
 
@@ -319,6 +352,9 @@ void ATTClass::handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[
 
     case ATT_OP_WRITE_REQ:
     case ATT_OP_WRITE_CMD:
+#ifdef _BLE_TRACE_
+      Serial.println("Write req");
+#endif
       writeReqOrCmd(connectionHandle, mtu, opcode, dlen, data);
       break;
 
@@ -346,6 +382,9 @@ void ATTClass::handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[
     case ATT_OP_READ_MULTI_REQ:
     case ATT_OP_SIGNED_WRITE_CMD:
     default:
+#ifdef _BLE_TRACE_
+      Serial.println("[Info] Unhandled dara");
+#endif
       sendError(connectionHandle, opcode, 0x00, ATT_ECODE_REQ_NOT_SUPP);
       break;
   }
@@ -398,6 +437,10 @@ void ATTClass::removeConnection(uint16_t handle, uint8_t /*reason*/)
   _peers[peerIndex].addressType = 0x00;
   memset(_peers[peerIndex].address, 0x00, sizeof(_peers[peerIndex].address));
   _peers[peerIndex].mtu = 23;
+  _peers[peerIndex].encryption = PEER_ENCRYPTION::NO_ENCRYPTION;
+  _peers[peerIndex].IOCap[0] = 0;
+  _peers[peerIndex].IOCap[1] = 0;
+  _peers[peerIndex].IOCap[2] = 0;
 
   if (_peers[peerIndex].device) {
     delete _peers[peerIndex].device;
@@ -456,6 +499,32 @@ bool ATTClass::connected(uint16_t handle) const
   return false;
 }
 
+/*
+ * Return true if any of the known devices is paired (peer encrypted)
+ * Does not check if the paired device is also connected
+ */
+bool ATTClass::paired() const
+{
+  for(int i=0; i<ATT_MAX_PEERS; i++){
+    if((_peers[i].encryption & PEER_ENCRYPTION::ENCRYPTED_AES) > 0){
+      return true;
+    }
+  }
+  return false;
+}
+
+/*
+ * Return true if the specified device is paired (peer encrypted)
+ */
+bool ATTClass::paired(uint16_t handle) const
+{  
+  for(int i=0; i<ATT_MAX_PEERS; i++){
+    if(_peers[i].connectionHandle != handle){continue;}
+    return (_peers[i].encryption & PEER_ENCRYPTION::ENCRYPTED_AES) > 0;
+  }
+  return false; // unknown handle
+}
+
 uint16_t ATTClass::mtu(uint16_t handle) const
 {
   for (int i = 0; i < ATT_MAX_PEERS; i++) {
@@ -486,6 +555,7 @@ bool ATTClass::disconnect()
     _peers[i].role = 0x00;
     _peers[i].addressType = 0x00;
     memset(_peers[i].address, 0x00, sizeof(_peers[i].address));
+    memset(_peers[i].resolvedAddress, 0x00, sizeof(_peers[i].resolvedAddress));
     _peers[i].mtu = 23;
 
     if (_peers[i].device) {
@@ -532,6 +602,7 @@ bool ATTClass::handleNotify(uint16_t handle, const uint8_t* value, int length)
     memcpy(&notification[notificationLength], value, length);
     notificationLength += length;
 
+    /// TODO: Set encryption requirement on notify.
     HCI.sendAclPkt(_peers[i].connectionHandle, ATT_CID, notificationLength, notification);
 
     numNotifications++;
@@ -808,6 +879,14 @@ void ATTClass::readByGroupReq(uint16_t connectionHandle, uint16_t mtu, uint8_t d
     uint16_t endHandle;
     uint16_t uuid;
   } *readByGroupReq = (ReadByGroupReq*)data;
+#ifdef _BLE_TRACE_
+  Serial.print("readByGroupReq: start: 0x");
+  Serial.println(readByGroupReq->startHandle,HEX);
+  Serial.print("readByGroupReq: end: 0x");
+  Serial.println(readByGroupReq->endHandle,HEX);
+  Serial.print("readByGroupReq: UUID: 0x");
+  Serial.println(readByGroupReq->uuid,HEX);
+#endif
 
   if (dlen != sizeof(ReadByGroupReq) || (readByGroupReq->uuid != BLETypeService && readByGroupReq->uuid != 0x2801)) {
     sendError(connectionHandle, ATT_OP_READ_BY_GROUP_REQ, readByGroupReq->startHandle, ATT_ECODE_UNSUPP_GRP_TYPE);
@@ -820,7 +899,10 @@ void ATTClass::readByGroupReq(uint16_t connectionHandle, uint16_t mtu, uint8_t d
   response[0] = ATT_OP_READ_BY_GROUP_RESP;
   response[1] = 0x00;
   responseLength = 2;
-
+#ifdef _BLE_TRACE_
+  Serial.print("readByGroupReq: attrcount: ");
+  Serial.println(GATT.attributeCount());
+#endif
   for (uint16_t i = (readByGroupReq->startHandle - 1); i < GATT.attributeCount() && i <= (readByGroupReq->endHandle - 1); i++) {
     BLELocalAttribute* attribute = GATT.attribute(i);
 
@@ -907,6 +989,8 @@ void ATTClass::readOrReadBlobReq(uint16_t connectionHandle, uint16_t mtu, uint8_
       return;
     }
   }
+  /// if auth error, hold the response in a buffer.
+  bool holdResponse = false;
 
   uint16_t handle = *(uint16_t*)data;
   uint16_t offset = (opcode == ATT_OP_READ_REQ) ? 0 : *(uint16_t*)&data[sizeof(handle)];
@@ -963,6 +1047,12 @@ void ATTClass::readOrReadBlobReq(uint16_t connectionHandle, uint16_t mtu, uint8_
         sendError(connectionHandle, opcode, handle, ATT_ECODE_READ_NOT_PERM);
         return;
       }
+      // If characteristic requires encryption send error & hold response until encrypted
+      if ((characteristic->permissions() & (BLEPermission::BLEEncryption >> 8)) > 0 &&
+          (getPeerEncryption(connectionHandle) & PEER_ENCRYPTION::ENCRYPTED_AES)==0 ) {
+        holdResponse = true;
+        sendError(connectionHandle, opcode, handle, ATT_ECODE_INSUFF_ENC);
+      }
 
       uint16_t valueLength = characteristic->valueLength();
 
@@ -995,8 +1085,12 @@ void ATTClass::readOrReadBlobReq(uint16_t connectionHandle, uint16_t mtu, uint8_
     memcpy(&response[responseLength], descriptor->value() + offset, valueLength);
     responseLength += valueLength;
   }
-  
-  HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response);
+  if(holdResponse){
+    memcpy(holdBuffer, response, responseLength);
+    holdBufferSize = responseLength;
+  }else{
+    HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response);
+  }
 }
 
 void ATTClass::readResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[])
@@ -1165,6 +1259,7 @@ void ATTClass::writeReqOrCmd(uint16_t connectionHandle, uint16_t mtu, uint8_t op
   uint8_t* value = &data[sizeof(handle)];
 
   BLELocalAttribute* attribute = GATT.attribute(handle - 1);
+  bool holdResponse = false;
 
   if (attribute->type() == BLETypeCharacteristic) {
     BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute;
@@ -1177,10 +1272,34 @@ void ATTClass::writeReqOrCmd(uint16_t connectionHandle, uint16_t mtu, uint8_t op
       }
       return;
     }
+    // Check permission
+    if((characteristic->permissions() &( BLEPermission::BLEEncryption >> 8)) > 0 && 
+       (getPeerEncryption(connectionHandle) & PEER_ENCRYPTION::ENCRYPTED_AES) == 0){
+      holdResponse = true;
+      sendError(connectionHandle, ATT_OP_WRITE_REQ, handle, ATT_ECODE_INSUFF_ENC);
+    }
 
     for (int i = 0; i < ATT_MAX_PEERS; i++) {
       if (_peers[i].connectionHandle == connectionHandle) {
-        characteristic->writeValue(BLEDevice(_peers[i].addressType, _peers[i].address), value, valueLength);
+        if(holdResponse){
+          
+          writeBufferSize = 0;
+          memcpy(writeBuffer, &handle, 2);
+          writeBufferSize+=2;
+
+          writeBuffer[writeBufferSize++] = _peers[i].addressType;
+
+          memcpy(&writeBuffer[writeBufferSize], _peers[i].address, sizeof(_peers[i].address));
+          writeBufferSize += sizeof(_peers[i].address);
+          
+          writeBuffer[writeBufferSize] = valueLength;
+          writeBufferSize += sizeof(valueLength);
+
+          memcpy(&writeBuffer[writeBufferSize], value, valueLength);
+          writeBufferSize += valueLength;
+        }else{
+          characteristic->writeValue(BLEDevice(_peers[i].addressType, _peers[i].address), value, valueLength);
+        }
         break;
       }
     }
@@ -1227,8 +1346,36 @@ void ATTClass::writeReqOrCmd(uint16_t connectionHandle, uint16_t mtu, uint8_t op
     response[0] = ATT_OP_WRITE_RESP;
     responseLength = 1;
 
-    HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response);
+    if(holdResponse){
+      memcpy(holdBuffer, response, responseLength);
+      holdBufferSize = responseLength;
+    }else{
+      HCI.sendAclPkt(connectionHandle, ATT_CID, responseLength, response);
+    }
+  }
+}
+int ATTClass::processWriteBuffer(){
+  if(writeBufferSize==0){
+    return 0;
   }
+
+  struct __attribute__ ((packed)) WriteBuffer {
+    uint16_t handle;
+    uint8_t addressType;
+    uint8_t address[6];
+    uint8_t valueLength;
+    uint8_t value[];
+  } *writeBufferStruct = (WriteBuffer*)&ATT.writeBuffer;
+  // uint8_t value[writeBufferStruct->valueLength];
+  // memcpy(value, writeBufferStruct->value, writeBufferStruct->valueLength);
+  BLELocalAttribute* attribute = GATT.attribute(writeBufferStruct->handle-1);
+  BLELocalCharacteristic* characteristic = (BLELocalCharacteristic*)attribute;
+#ifdef _BLE_TRACE_
+  Serial.println("Writing value");
+#endif
+  characteristic->writeValue(BLEDevice(writeBufferStruct->addressType, writeBufferStruct->address), writeBufferStruct->value, writeBufferStruct->valueLength);
+  writeBufferSize = 0;
+  return 1;
 }
 
 void ATTClass::writeResp(uint16_t connectionHandle, uint8_t dlen, uint8_t data[])
@@ -1359,14 +1506,14 @@ void ATTClass::handleNotifyOrInd(uint16_t connectionHandle, uint8_t opcode, uint
     uint16_t handle;
   } *handleNotifyOrInd = (HandleNotifyOrInd*)data;
 
-  uint8_t handle = handleNotifyOrInd->handle;
+  uint16_t handle = handleNotifyOrInd->handle;
 
-  for (int i = 0; i < ATT_MAX_PEERS; i++) {
-    if (_peers[i].connectionHandle != connectionHandle) {
+  for (int peer = 0; peer < ATT_MAX_PEERS; peer++) {
+    if (_peers[peer].connectionHandle != connectionHandle) {
       continue;
     }
 
-    BLERemoteDevice* device = _peers[i].device;
+    BLERemoteDevice* device = _peers[peer].device;
 
     if (!device) {
       break;
@@ -1384,7 +1531,7 @@ void ATTClass::handleNotifyOrInd(uint16_t connectionHandle, uint8_t opcode, uint
           BLERemoteCharacteristic* c = s->characteristic(j);
 
           if (c->valueHandle() == handle) {
-            c->writeValue(BLEDevice(_peers[i].addressType, _peers[i].address), &data[2], dlen - 2);
+            c->writeValue(BLEDevice(_peers[peer].addressType, _peers[peer].address), &data[2], dlen - 2);
           }
         }
 
@@ -1579,8 +1726,9 @@ bool ATTClass::discoverDescriptors(uint16_t connectionHandle, BLERemoteDevice* d
         }
 
         if (responseBuffer[0] == ATT_OP_FIND_INFO_RESP) {
-          uint16_t lengthPerDescriptor = responseBuffer[1] * 4;
-          uint8_t uuidLen = 2;
+          uint8_t uuidLen = responseBuffer[1] == 1 ? 2 : 16; 
+          uint16_t lengthPerDescriptor = uuidLen + 2;
+ 
 
           for (int i = 2; i < respLength; i += lengthPerDescriptor) {
             struct __attribute__ ((packed)) RawDescriptor {
@@ -1688,6 +1836,108 @@ void ATTClass::writeCmd(uint16_t connectionHandle, uint16_t handle, const uint8_
   sendReq(connectionHandle, &writeReq, 3 + dataLen, NULL);
 }
 
+// Set encryption state for a peer
+int ATTClass::setPeerEncryption(uint16_t connectionHandle, uint8_t encryption){
+  for(int i=0; i<ATT_MAX_PEERS; i++){
+    if(_peers[i].connectionHandle != connectionHandle){
+      continue;
+    }
+    _peers[i].encryption = encryption;
+    return 1;
+  }
+  return 0;
+}
+// Set the IO capabilities for a peer
+int ATTClass::setPeerIOCap(uint16_t connectionHandle, uint8_t IOCap[3]){
+  for(int i=0; i<ATT_MAX_PEERS; i++){
+    if(_peers[i].connectionHandle != connectionHandle){
+      continue;
+    }
+    memcpy(_peers[i].IOCap, IOCap, 3);
+    return 1;
+  }
+  return 0;
+}
+// Return the connection handle for the first peer that is requesting encryption
+uint16_t ATTClass::getPeerEncrptingConnectionHandle(){
+  for(int i=0; i<ATT_MAX_PEERS; i++){
+    if((_peers[i].encryption & PEER_ENCRYPTION::REQUESTED_ENCRYPTION) > 0){
+      return _peers[i].connectionHandle;
+    }
+  }
+  return ATT_MAX_PEERS + 1;
+}
+// Get the encryption state for a particular peer / connection handle
+uint8_t ATTClass::getPeerEncryption(uint16_t connectionHandle) {
+  for(int i=0; i<ATT_MAX_PEERS; i++){
+    if(_peers[i].connectionHandle != connectionHandle){continue;}
+    return _peers[i].encryption;
+  }
+  return 0;
+}
+// Get the IOCapabilities for a peer
+int ATTClass::getPeerIOCap(uint16_t connectionHandle, uint8_t IOCap[3]) {
+  for(int i=0; i<ATT_MAX_PEERS; i++){
+    if(_peers[i].connectionHandle != connectionHandle){continue;}
+    // return _peers[i].encryption;
+    memcpy(IOCap, _peers[i].IOCap, 3);
+  }
+  return 0;
+}
+// Get the BD_ADDR for a peer
+int ATTClass::getPeerAddr(uint16_t connectionHandle, uint8_t peerAddr[])
+{
+  for(int i=0; i<ATT_MAX_PEERS; i++)
+  {
+    if(_peers[i].connectionHandle != connectionHandle){continue;}
+    memcpy(peerAddr, _peers[i].address,6);
+    return 1;
+  }
+  return 0;
+}
+// Get the BD_ADDR for a peer in the format needed by f6 for pairing.
+int ATTClass::getPeerAddrWithType(uint16_t connectionHandle, uint8_t peerAddr[])
+{
+  for(int i=0; i<ATT_MAX_PEERS; i++)
+  {
+    if(_peers[i].connectionHandle != connectionHandle){continue;}
+    for(int k=0; k<6; k++){
+      peerAddr[6-k] = _peers[i].address[k];
+    }
+    if(_peers[i].addressType){
+      peerAddr[0] = _peers[i].addressType;
+    }else{
+      peerAddr[0] = 0x00;
+    }
+    return 1;
+  }
+  return 0;
+}
+// Get the resolved address for a peer if it exists
+int ATTClass::getPeerResolvedAddress(uint16_t connectionHandle, uint8_t resolvedAddress[]){
+  for(int i=0; i<ATT_MAX_PEERS; i++)
+  {
+    if(_peers[i].connectionHandle != connectionHandle){continue;}
+
+    bool allZero=true;
+    for(int k=0; k<6; k++){
+      if(_peers[i].resolvedAddress[k] == 0){
+        continue;
+      }else{
+        allZero = false;
+        break;
+      }
+    }
+
+    if(allZero){
+      return 0;
+    }
+
+    memcpy(resolvedAddress, _peers[i].resolvedAddress, 6);
+    return 1;
+  }
+  return 0;
+}
 void ATTClass::setOwnBdaddrType(uint8_t ownBdaddrType)
 {
   _ownBdaddrType = ownBdaddrType;
diff --git a/src/utility/ATT.h b/src/utility/ATT.h
index eba4a76b..0067d45a 100644
--- a/src/utility/ATT.h
+++ b/src/utility/ATT.h
@@ -23,8 +23,10 @@
 #include <Arduino.h>
 
 #include "BLEDevice.h"
+#include "keyDistribution.h"
 
 #define ATT_CID       0x0004
+#define BLE_CTL       0x0008
 
 #if DM_CONN_MAX
 #define ATT_MAX_PEERS DM_CONN_MAX // Mbed + Cordio
@@ -34,6 +36,17 @@
 #define ATT_MAX_PEERS 8
 #endif
 
+enum PEER_ENCRYPTION {
+  NO_ENCRYPTION         = 0,
+  PAIRING_REQUEST       = 1 << 0,
+  REQUESTED_ENCRYPTION  = 1 << 1,
+  SENT_PUBKEY           = 1 << 2,
+  DH_KEY_CALULATED      = 1 << 3,
+  RECEIVED_DH_CHECK     = 1 << 4,
+  SENT_DH_CHECK         = 1 << 5,
+  ENCRYPTED_AES         = 1 << 7
+};
+
 class BLERemoteDevice;
 
 class ATTClass {
@@ -62,6 +75,8 @@ class ATTClass {
   virtual bool connected() const;
   virtual bool connected(uint8_t addressType, const uint8_t address[6]) const;
   virtual bool connected(uint16_t handle) const;
+  virtual bool paired() const;
+  virtual bool paired(uint16_t handle) const;
   virtual uint16_t mtu(uint16_t handle) const;
 
   virtual bool disconnect();
@@ -76,9 +91,27 @@ class ATTClass {
   virtual int readReq(uint16_t connectionHandle, uint16_t handle, uint8_t responseBuffer[]);
   virtual int writeReq(uint16_t connectionHandle, uint16_t handle, const uint8_t* data, uint8_t dataLen, uint8_t responseBuffer[]);
   virtual void writeCmd(uint16_t connectionHandle, uint16_t handle, const uint8_t* data, uint8_t dataLen);
+  virtual int setPeerEncryption(uint16_t connectionHandle, uint8_t encryption);
+  uint8_t getPeerEncryption(uint16_t connectionHandle);
+  uint16_t getPeerEncrptingConnectionHandle();
+  virtual int getPeerAddr(uint16_t connectionHandle, uint8_t peerAddr[]);
+  virtual int getPeerAddrWithType(uint16_t connectionHandle, uint8_t peerAddr[]);
+  virtual int setPeerIOCap(uint16_t connectionHandle, uint8_t IOCap[]);
+  virtual int getPeerIOCap(uint16_t connectionHandle, uint8_t IOCap[]);
+  virtual int getPeerResolvedAddress(uint16_t connectionHandle, uint8_t* resolvedAddress);
+  uint8_t holdBuffer[64];
+  uint8_t writeBuffer[64];
+  uint8_t holdBufferSize;
+  uint8_t writeBufferSize;
+  virtual int processWriteBuffer();
+  KeyDistribution remoteKeyDistribution;
+  KeyDistribution localKeyDistribution;
+  uint8_t peerIRK[16];
+  /// This is just a random number... Not sure it has use unless privacy mode is active.
+  uint8_t localIRK[16] = {0x54,0x83,0x63,0x7c,0xc5,0x1e,0xf7,0xec,0x32,0xdd,0xad,0x51,0x89,0x4b,0x9e,0x07};
 
   void setOwnBdaddrType(uint8_t ownBdaddrType);
-
+  uint8_t _ownBdaddrType; //@note Used in L2CAPSignaling to encryption 
 private:
   virtual void error(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]);
   virtual void mtuReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]);
@@ -119,8 +152,11 @@ class ATTClass {
     uint8_t role;
     uint8_t addressType;
     uint8_t address[6];
+    uint8_t resolvedAddress[6];
     uint16_t mtu;
     BLERemoteDevice* device;
+    uint8_t encryption;
+    uint8_t IOCap[3];
   } _peers[ATT_MAX_PEERS];
 
   volatile bool _cnf;
@@ -137,8 +173,6 @@ class ATTClass {
   } _pendingResp;
 
   BLEDeviceEventHandler _eventHandlers[2];
-
-  uint8_t _ownBdaddrType;
 };
 
 extern ATTClass& ATT;
diff --git a/src/utility/GAP.cpp b/src/utility/GAP.cpp
index 9bdb10b1..85b9f244 100644
--- a/src/utility/GAP.cpp
+++ b/src/utility/GAP.cpp
@@ -228,8 +228,10 @@ void GAPClass::handleLeAdvertisingReport(uint8_t type, uint8_t addressType, uint
 
   if (discoveredDevice == NULL) {
     if (_discoveredDevices.size() >= GAP_MAX_DISCOVERED_QUEUE_SIZE) {
-      // drop
-      return;
+      BLEDevice* device_first = _discoveredDevices.remove(0);
+      if (device_first != NULL) {
+        delete device_first;
+      }
     }
 
     discoveredDevice = new BLEDevice(addressType, address);
diff --git a/src/utility/GATT.cpp b/src/utility/GATT.cpp
index 373213b9..be914f6f 100644
--- a/src/utility/GATT.cpp
+++ b/src/utility/GATT.cpp
@@ -38,7 +38,7 @@ GATTClass::GATTClass() :
 
 GATTClass::~GATTClass()
 {
-  clearAttributes();
+  end();
 }
 
 void GATTClass::begin()
@@ -70,7 +70,22 @@ void GATTClass::begin()
 
 void GATTClass::end()
 {
-  _attributes.clear();
+  if (_genericAccessService->release() == 0)
+    delete(_genericAccessService);
+  
+  if (_deviceNameCharacteristic->release() == 0)
+    delete(_deviceNameCharacteristic);
+  
+  if (_appearanceCharacteristic->release() == 0)
+    delete(_appearanceCharacteristic);
+  
+  if (_genericAttributeService->release() == 0)
+    delete(_genericAttributeService);
+  
+  if (_servicesChangedCharacteristic->release() == 0)
+    delete(_servicesChangedCharacteristic);
+  
+  clearAttributes();
 }
 
 void GATTClass::setDeviceName(const char* deviceName)
@@ -134,6 +149,7 @@ void GATTClass::addService(BLELocalService* service)
 {
   service->retain();
   _attributes.add(service);
+  _services.add(service);
 
   uint16_t startHandle = attributeCount();
 
@@ -145,6 +161,7 @@ void GATTClass::addService(BLELocalService* service)
     characteristic->setHandle(attributeCount());
     
     // add the characteristic again to make space of the characteristic value handle
+    characteristic->retain();
     _attributes.add(characteristic);
 
     for (unsigned int j = 0; j < characteristic->descriptorCount(); j++) {
@@ -164,12 +181,17 @@ void GATTClass::clearAttributes()
   for (unsigned int i = 0; i < attributeCount(); i++) {
     BLELocalAttribute* a = attribute(i);
 
-    if (a->release() <= 0) {
+    if (a->release() == 0) {
       delete a;
     }
   }
-
   _attributes.clear();
+
+  for (unsigned int i = 0; i < _services.size(); i++) {
+    _services.get(i)->clear();
+  }
+  _services.clear();
+
 }
 
 #if !defined(FAKE_GATT)
diff --git a/src/utility/GATT.h b/src/utility/GATT.h
index fd916709..51d1537e 100644
--- a/src/utility/GATT.h
+++ b/src/utility/GATT.h
@@ -59,6 +59,7 @@ class GATTClass {
 
 private:
   BLELinkedList<BLELocalAttribute*> _attributes;
+  BLELinkedList<BLELocalService*>   _services;
 
   BLELocalService*              _genericAccessService;
   BLELocalCharacteristic*       _deviceNameCharacteristic;
diff --git a/src/utility/HCI.cpp b/src/utility/HCI.cpp
index f9c7d9c1..f4eabc1a 100644
--- a/src/utility/HCI.cpp
+++ b/src/utility/HCI.cpp
@@ -20,27 +20,29 @@
 #include "ATT.h"
 #include "GAP.h"
 #include "L2CAPSignaling.h"
-
+#include "btct.h"
 #include "HCI.h"
+#include "bitDescriptions.h"
+// #define _BLE_TRACE_
+
 
-#define HCI_COMMAND_PKT 0x01
-#define HCI_ACLDATA_PKT 0x02
-#define HCI_EVENT_PKT   0x04
+#define HCI_COMMAND_PKT   0x01
+#define HCI_ACLDATA_PKT   0x02
+#define HCI_EVENT_PKT     0x04
+#define HCI_SECURITY_PKT  0x06
 
-#define EVT_DISCONN_COMPLETE 0x05
-#define EVT_CMD_COMPLETE     0xe
-#define EVT_CMD_STATUS       0x0f
-#define EVT_NUM_COMP_PKTS    0x13
-#define EVT_LE_META_EVENT    0x3e
+#define EVT_DISCONN_COMPLETE  0x05
+#define EVT_ENCRYPTION_CHANGE 0x08
+#define EVT_CMD_COMPLETE      0x0e
+#define EVT_CMD_STATUS        0x0f
+#define EVT_NUM_COMP_PKTS     0x13
+#define EVT_RETURN_LINK_KEYS  0x15
+#define EVT_UNKNOWN           0x10
+#define EVT_LE_META_EVENT     0x3e
 
 #define EVT_LE_CONN_COMPLETE      0x01
 #define EVT_LE_ADVERTISING_REPORT 0x02
 
-#define OGF_LINK_CTL           0x01
-#define OGF_HOST_CTL           0x03
-#define OGF_INFO_PARAM         0x04
-#define OGF_STATUS_PARAM       0x05
-#define OGF_LE_CTL             0x08
 
 // OGF_LINK_CTL
 #define OCF_DISCONNECT         0x0006
@@ -72,6 +74,29 @@
 
 #define HCI_OE_USER_ENDED_CONNECTION 0x13
 
+String metaEventToString(LE_META_EVENT event)
+{
+  switch(event){
+    case CONN_COMPLETE: return F("CONN_COMPLETE");
+    case ADVERTISING_REPORT: return F("ADVERTISING_REPORT");
+    case LONG_TERM_KEY_REQUEST: return F("LE_LONG_TERM_KEY_REQUEST");
+    case READ_LOCAL_P256_COMPLETE: return F("READ_LOCAL_P256_COMPLETE");
+    case GENERATE_DH_KEY_COMPLETE: return F("GENERATE_DH_KEY_COMPLETE");
+    default: return "event unknown";
+  }
+}
+String commandToString(LE_COMMAND command){
+  switch (command)
+  {
+    case ENCRYPT: return F("ENCRYPT");
+    case LONG_TERM_KEY_REPLY: return F("LONG_TERM_KEY_REPLY");
+    case READ_LOCAL_P256: return F("READ_LOCAL_P256");
+    case GENERATE_DH_KEY_V1: return F("GENERATE_DH_KEY_V1");
+    case GENERATE_DH_KEY_V2: return F("GENERATE_DH_KEY_V2");
+    default: return "UNKNOWN";
+  }
+}
+
 HCIClass::HCIClass() :
   _debug(NULL),
   _recvIndex(0),
@@ -110,6 +135,13 @@ void HCIClass::poll(unsigned long timeout)
 
   while (_HCITransport->available()) {
     byte b = _HCITransport->read();
+	
+    if (_recvIndex >= sizeof(_recvBuffer)) {
+        _recvIndex = 0;
+        if (_debug) {
+            _debug->println("_recvBuffer overflow");
+        }
+    }
 
     _recvBuffer[_recvIndex++] = b;
 
@@ -185,6 +217,17 @@ int HCIClass::readBdAddr(uint8_t addr[6])
   return result;
 }
 
+int HCIClass::readBdAddr(){
+  uint8_t response[6];
+  int result = readBdAddr(response);
+  if(result==0){
+    for(int i=0; i<6; i++){
+      localAddr[5-i] = _cmdResponse[i];
+    }
+  }
+  return result;
+}
+
 int HCIClass::readRssi(uint16_t handle)
 {
   int result = sendCommand(OGF_STATUS_PARAM << 10 | OCF_READ_RSSI, sizeof(handle), &handle);
@@ -208,6 +251,11 @@ int HCIClass::setEventMask(uint64_t eventMask)
 {
   return sendCommand(OGF_HOST_CTL << 10 | OCF_SET_EVENT_MASK, sizeof(eventMask), &eventMask);
 }
+// Set LE Event mask
+int HCIClass::setLeEventMask(uint64_t leEventMask)
+{
+  return sendCommand(OGF_LE_CTL << 10 | 0x01, sizeof(leEventMask), &leEventMask);
+}
 
 int HCIClass::readLeBufferSize(uint16_t& pktLen, uint8_t& maxPkt)
 {
@@ -392,7 +440,158 @@ int HCIClass::leConnUpdate(uint16_t handle, uint16_t minInterval, uint16_t maxIn
 
   return sendCommand(OGF_LE_CTL << 10 | OCF_LE_CONN_UPDATE, sizeof(leConnUpdateData), &leConnUpdateData);
 }
+void HCIClass::saveNewAddress(uint8_t addressType, uint8_t* address, uint8_t* peerIrk, uint8_t* localIrk){
+  if(_storeIRK!=0){
+    _storeIRK(address, peerIrk);
+  }
+  // Again... this should work 
+  // leAddResolvingAddress(addressType, address, peerIrk, localIrk);
+}
+void HCIClass::leAddResolvingAddress(uint8_t addressType, uint8_t* peerAddress, uint8_t* peerIrk, uint8_t* localIrk){
+  leStopResolvingAddresses();
+
+  struct __attribute__ ((packed)) AddDevice {
+    uint8_t peerAddressType;
+    uint8_t peerAddress[6];
+    uint8_t peerIRK[16];
+    uint8_t localIRK[16];
+  } addDevice;
+  addDevice.peerAddressType = addressType;
+  for(int i=0; i<6; i++) addDevice.peerAddress[5-i] = peerAddress[i];
+  for(int i=0; i<16; i++) {
+    addDevice.peerIRK[15-i]  = peerIrk[i];
+    addDevice.localIRK[15-i] = localIrk[i];
+  }
+  Serial.print("ADDTYPE    :");
+  btct.printBytes(&addDevice.peerAddressType,1);
+  Serial.print("adddddd    :");
+  btct.printBytes(addDevice.peerAddress,6);
+  Serial.print("Peer IRK   :");
+  btct.printBytes(addDevice.peerIRK,16);
+  Serial.print("localIRK   :");
+  btct.printBytes(addDevice.localIRK,16);
+  sendCommand(OGF_LE_CTL << 10 | 0x27, sizeof(addDevice), &addDevice); 
+
+  leStartResolvingAddresses();
+}
+int HCIClass::leStopResolvingAddresses(){
+    uint8_t enable = 0;
+    return HCI.sendCommand(OGF_LE_CTL << 10 | 0x2D, 1,&enable); // Disable address resolution
+}
+int HCIClass::leStartResolvingAddresses(){
+  uint8_t enable = 1;
+  return HCI.sendCommand(OGF_LE_CTL << 10 | 0x2D, 1,&enable); // Disable address resolution
+}
+int HCIClass::leReadPeerResolvableAddress(uint8_t peerAddressType, uint8_t* peerIdentityAddress, uint8_t* peerResolvableAddress){
+  struct __attribute__ ((packed)) Request {
+    uint8_t addressType;
+    uint8_t identityAddress[6];
+  } request;
+  request.addressType = peerAddressType;
+  for(int i=0; i<6; i++) request.identityAddress[5-i] = peerIdentityAddress[i];
+  
+
+  int res = sendCommand(OGF_LE_CTL << 10 | 0x2B, sizeof(request), &request);
+  Serial.print("res: 0x");
+  Serial.println(res, HEX);
+  if(res==0){
+    struct __attribute__ ((packed)) Response {
+      uint8_t status;
+      uint8_t peerResolvableAddress[6];
+    } *response = (Response*)_cmdResponse;
+    Serial.print("Address resolution status: 0x");
+    Serial.println(response->status, HEX);
+    Serial.print("peer resolvable address: ");
+    btct.printBytes(response->peerResolvableAddress,6);
+  }
+  return res;
+}
 
+void HCIClass::writeLK(uint8_t peerAddress[], uint8_t LK[]){
+  struct __attribute__ ((packed)) StoreLK {
+    uint8_t nKeys;
+    uint8_t BD_ADDR[6];
+    uint8_t LTK[16];
+  } storeLK;
+  storeLK.nKeys = 1;
+  memcpy(storeLK.BD_ADDR, peerAddress, 6);
+  for(int i=0; i<16; i++) storeLK.LTK[15-i] = LK[i];
+  HCI.sendCommand(OGF_HOST_CTL << 10 | 0x11, sizeof(storeLK), &storeLK);
+}
+void HCIClass::readStoredLKs(){
+  uint8_t BD_ADDR[6];
+  readStoredLK(BD_ADDR, 1);
+}
+int HCIClass::readStoredLK(uint8_t BD_ADDR[], uint8_t read_all ){
+  struct __attribute__ ((packed)) Request {
+    uint8_t BD_ADDR[6];
+    uint8_t read_a;
+  } request = {0,0};
+  for(int i=0; i<6; i++) request.BD_ADDR[5-i] = BD_ADDR[i];
+  request.read_a = read_all;
+  return sendCommand(OGF_HOST_CTL << 10 | 0xD, sizeof(request), &request);
+}
+
+int HCIClass::tryResolveAddress(uint8_t* BDAddr, uint8_t* address){
+  bool foundMatch = false;
+  if(HCI._getIRKs!=0){
+    uint8_t nIRKs = 0;
+    uint8_t** BDAddrType = new uint8_t*;
+    uint8_t*** BADDRs = new uint8_t**;
+    uint8_t*** IRKs = new uint8_t**;
+
+
+    if(!HCI._getIRKs(&nIRKs, BDAddrType, BADDRs, IRKs)){
+      Serial.println("error getting IRKs.");
+    }
+    for(int i=0; i<nIRKs; i++){
+      if(!foundMatch){
+#ifdef _BLE_TRACE_
+        Serial.print("BDAddr type:      : 0x");
+        Serial.println((*BDAddrType)[i],HEX);
+        Serial.print("BDAddr            : ");
+        btct.printBytes((*BADDRs)[i],6);
+        Serial.print("IRK               : ");
+        btct.printBytes((*IRKs)[i],16);
+#endif
+        uint8_t hashresult[3];
+        btct.ah((*IRKs)[i], BDAddr, hashresult);
+#ifdef _BLE_TRACE_
+        Serial.print("hash match        : ");
+        btct.printBytes(hashresult,3);
+        Serial.print("                  : ");
+        btct.printBytes(&BDAddr[3],3);
+#endif
+
+        for(int k=0; k<3; k++){
+          if(hashresult[k] == BDAddr[3 + k]){
+            foundMatch = true;
+          }else{
+            foundMatch = false;
+            break;
+          }
+        }
+        if(foundMatch){
+          memcpy(address, (*BADDRs)[i],6);
+        }
+      }
+      delete[] (*BADDRs)[i];
+      delete[] (*IRKs)[i];
+    }
+    delete[] (*BDAddrType);
+    delete BDAddrType;
+    delete[] (*BADDRs);
+    delete BADDRs;
+    delete[] (*IRKs);
+    delete IRKs;
+    
+    if(foundMatch){
+      return 1;
+    }
+  }
+  return 0;
+}
+/*
 int HCIClass::leRand(uint8_t randomNumber[8])
 {
   int result = sendCommand(OGF_LE_CTL << 10 | OCF_LE_RAND);
@@ -403,8 +602,8 @@ int HCIClass::leRand(uint8_t randomNumber[8])
 
   return result;
 }
-
-int HCIClass::sendAclPkt(uint16_t handle, uint8_t cid, uint8_t plen, void* data)
+*/
+int HCIClass::sendAclPkt(uint16_t handle, uint8_t cid, uint8_t plen, const void* data)
 {
   while (_pendingPkt >= _maxPkt) {
     poll();
@@ -418,13 +617,21 @@ int HCIClass::sendAclPkt(uint16_t handle, uint8_t cid, uint8_t plen, void* data)
     uint16_t cid;
   } aclHdr = { HCI_ACLDATA_PKT, handle, uint8_t(plen + 4), plen, cid };
 
-  uint8_t txBuffer[sizeof(aclHdr) + plen];
+  uint8_t* txBuffer = (uint8_t*)malloc(sizeof(aclHdr) + plen);
   memcpy(txBuffer, &aclHdr, sizeof(aclHdr));
   memcpy(&txBuffer[sizeof(aclHdr)], data, plen);
 
   if (_debug) {
     dumpPkt("HCI ACLDATA TX -> ", sizeof(aclHdr) + plen, txBuffer);
   }
+#ifdef _BLE_TRACE_
+  Serial.print("Data tx -> ");
+  for(int i=0; i< sizeof(aclHdr) + plen;i++){
+    Serial.print(" 0x");
+    Serial.print(txBuffer[i],HEX);
+  }
+  Serial.println(".");
+#endif
 
   _pendingPkt++;
   _HCITransport->write(txBuffer, sizeof(aclHdr) + plen);
@@ -452,7 +659,7 @@ void HCIClass::noDebug()
   _debug = NULL;
 }
 
-int HCIClass::sendCommand(uint16_t opcode, uint8_t plen, void* parameters)
+int HCIClass::sendCommand(uint16_t opcode, uint8_t plen, const void* parameters)
 {
   struct __attribute__ ((packed)) {
     uint8_t pktType;
@@ -460,13 +667,22 @@ int HCIClass::sendCommand(uint16_t opcode, uint8_t plen, void* parameters)
     uint8_t plen;
   } pktHdr = {HCI_COMMAND_PKT, opcode, plen};
 
-  uint8_t txBuffer[sizeof(pktHdr) + plen];
+  uint8_t* txBuffer = (uint8_t*)malloc(sizeof(pktHdr) + plen);
   memcpy(txBuffer, &pktHdr, sizeof(pktHdr));
   memcpy(&txBuffer[sizeof(pktHdr)], parameters, plen);
 
   if (_debug) {
     dumpPkt("HCI COMMAND TX -> ", sizeof(pktHdr) + plen, txBuffer);
   }
+#ifdef _BLE_TRACE_
+  Serial.print("Command tx -> ");
+  for(int i=0; i< sizeof(pktHdr) + plen;i++){
+    Serial.print(" 0x");
+    Serial.print(txBuffer[i],HEX);
+    
+  }
+  Serial.println("");
+#endif
 
   _HCITransport->write(txBuffer, sizeof(pktHdr) + plen);
 
@@ -489,6 +705,7 @@ void HCIClass::handleAclDataPkt(uint8_t /*plen*/, uint8_t pdata[])
     uint16_t cid;
   } *aclHdr = (HCIACLHdr*)pdata;
 
+
   uint16_t aclFlags = (aclHdr->handle & 0xf000) >> 12;
 
   if ((aclHdr->dlen - 4) != aclHdr->len) {
@@ -508,6 +725,17 @@ void HCIClass::handleAclDataPkt(uint8_t /*plen*/, uint8_t pdata[])
   }
 
   if ((aclHdr->dlen - 4) != aclHdr->len) {
+#ifdef _BLE_TRACE_
+    Serial.println("Don't have full packet yet");
+    Serial.print("Handle: ");
+    btct.printBytes((uint8_t*)&aclHdr->handle,2);
+    Serial.print("dlen: ");
+    btct.printBytes((uint8_t*)&aclHdr->dlen,2);
+    Serial.print("len: ");
+    btct.printBytes((uint8_t*)&aclHdr->len,2);
+    Serial.print("cid: ");
+    btct.printBytes((uint8_t*)&aclHdr->cid,2);
+#endif
     // don't have the full packet yet
     return;
   }
@@ -521,8 +749,22 @@ void HCIClass::handleAclDataPkt(uint8_t /*plen*/, uint8_t pdata[])
       ATT.handleData(aclHdr->handle & 0x0fff, aclHdr->len, &_recvBuffer[1 + sizeof(HCIACLHdr)]);
     }
   } else if (aclHdr->cid == SIGNALING_CID) {
+#ifdef _BLE_TRACE_
+    Serial.println("Signalling");
+#endif
     L2CAPSignaling.handleData(aclHdr->handle & 0x0fff, aclHdr->len, &_recvBuffer[1 + sizeof(HCIACLHdr)]);
-  } else {
+  } else if (aclHdr->cid == SECURITY_CID){
+    // Security manager
+#ifdef _BLE_TRACE_
+    Serial.println("Security data");
+#endif
+    if (aclFlags == 0x1){
+      L2CAPSignaling.handleSecurityData(aclHdr->handle & 0x0fff, aclHdr->len, &_aclPktBuffer[sizeof(HCIACLHdr)]);
+    }else{
+      L2CAPSignaling.handleSecurityData(aclHdr->handle & 0x0fff, aclHdr->len, &_recvBuffer[1 + sizeof(HCIACLHdr)]);
+    }
+
+  }else {
     struct __attribute__ ((packed)) {
       uint8_t op;
       uint8_t id;
@@ -531,6 +773,10 @@ void HCIClass::handleAclDataPkt(uint8_t /*plen*/, uint8_t pdata[])
       uint16_t localCid;
       uint16_t remoteCid;
     } l2capRejectCid= { 0x01, 0x00, 0x006, 0x0002, aclHdr->cid, 0x0000 };
+#ifdef _BLE_TRACE_
+    Serial.print("rejecting packet cid: 0x");
+    Serial.println(aclHdr->cid,HEX);
+#endif
 
     sendAclPkt(aclHdr->handle & 0x0fff, 0x0005, sizeof(l2capRejectCid), &l2capRejectCid);
   }
@@ -551,8 +797,13 @@ void HCIClass::handleEventPkt(uint8_t /*plen*/, uint8_t pdata[])
     uint8_t evt;
     uint8_t plen;
   } *eventHdr = (HCIEventHdr*)pdata;
+#ifdef _BLE_TRACE_
+  Serial.print("HCI event: ");
+  Serial.println(eventHdr->evt, HEX);
+#endif
 
-  if (eventHdr->evt == EVT_DISCONN_COMPLETE) {
+  if (eventHdr->evt == EVT_DISCONN_COMPLETE)
+  {
     struct __attribute__ ((packed)) DisconnComplete {
       uint8_t status;
       uint16_t handle;
@@ -562,101 +813,647 @@ void HCIClass::handleEventPkt(uint8_t /*plen*/, uint8_t pdata[])
     ATT.removeConnection(disconnComplete->handle, disconnComplete->reason);
     L2CAPSignaling.removeConnection(disconnComplete->handle, disconnComplete->reason);
 
-    if(GAP.advertising()) {
-      HCI.leSetAdvertiseEnable(0x01);
+    if (GAP.advertising())
+    {
+          HCI.leSetAdvertiseEnable(0x01);
+    }
+      }
+  else if (eventHdr->evt == EVT_ENCRYPTION_CHANGE)
+  {
+    struct __attribute__ ((packed)) EncryptionChange {
+      uint8_t status;
+      uint16_t connectionHandle;
+      uint8_t enabled;
+    } *encryptionChange = (EncryptionChange*)&pdata[sizeof(HCIEventHdr)];
+#ifdef _BLE_TRACE_
+    Serial.println("[Info] Encryption changed");
+    Serial.print("status : ");
+    btct.printBytes(&encryptionChange->status,1);
+    Serial.print("handle : ");
+    btct.printBytes((uint8_t*)&encryptionChange->connectionHandle,2);
+    Serial.print("enabled: ");
+    btct.printBytes(&encryptionChange->enabled,1);
+#endif
+    if(encryptionChange->enabled>0){
+      // 0001 1110
+      if((ATT.getPeerEncryption(encryptionChange->connectionHandle)&PEER_ENCRYPTION::PAIRING_REQUEST)>0){
+        if(ATT.localKeyDistribution.EncKey()){
+#ifdef _BLE_TRACE_
+          Serial.println("Enc key set but should be ignored");
+#endif
+        }else{
+#ifdef _BLE_TRACE_
+          Serial.println("No enc key distribution");
+#endif
+        }
+        // From page 1681 bluetooth standard - order matters
+        if(ATT.localKeyDistribution.IdKey()){
+          /// We shall distribute IRK and address using identity information 
+          {
+            uint8_t response[17];
+            response[0] = CONNECTION_IDENTITY_INFORMATION; // Identity information.
+            for(int i=0; i<16; i++) response[16-i] = ATT.localIRK[i];
+            HCI.sendAclPkt(encryptionChange->connectionHandle, SECURITY_CID, sizeof(response), response);
+#ifdef _BLE_TRACE_
+            Serial.println("Distribute ID Key");
+#endif
+          }
+          {
+            uint8_t response[8];
+            response[0] = CONNECTION_IDENTITY_ADDRESS; // Identity address information
+            response[1] = 0x00; // Static local address
+            for(int i=0; i<6; i++) response[7-i] = HCI.localAddr[i];
+            HCI.sendAclPkt(encryptionChange->connectionHandle, SECURITY_CID, sizeof(response), response);
+          }
+        }
+        if(ATT.localKeyDistribution.SignKey()){
+          /// We shall distribut CSRK
+#ifdef _BLE_TRACE_
+          Serial.println("We shall distribute CSRK // not implemented");
+#endif
+
+        }else{
+          // Serial.println("We don't want to distribute CSRK");
+        }
+        if(ATT.localKeyDistribution.LinkKey()){
+#ifdef _BLE_TRACE_
+          Serial.println("We would like to use LTK to generate BR/EDR // not implemented");
+#endif
+        }
+      }else{
+#ifdef _BLE_TRACE_
+        Serial.println("Reconnection, not pairing so no keys");
+        Serial.println(ATT.getPeerEncryption(encryptionChange->connectionHandle),HEX);
+#endif
+      }
+
+      ATT.setPeerEncryption(encryptionChange->connectionHandle, PEER_ENCRYPTION::ENCRYPTED_AES);
+      if(ATT.writeBufferSize > 0){
+        ATT.processWriteBuffer();
+      }
+      if(ATT.holdBufferSize>0){
+#ifdef _BLE_TRACE_
+        Serial.print("Sending queued response size: ");
+        Serial.println(ATT.holdBufferSize);
+#endif
+        HCI.sendAclPkt(encryptionChange->connectionHandle, ATT_CID, ATT.holdBufferSize, ATT.holdBuffer);
+        ATT.holdBufferSize = 0;
+      }
+    }else{
+      ATT.setPeerEncryption(encryptionChange->connectionHandle, PEER_ENCRYPTION::NO_ENCRYPTION);
     }
-  } else if (eventHdr->evt == EVT_CMD_COMPLETE) {
+  }
+  else if (eventHdr->evt == EVT_CMD_COMPLETE)
+  {
     struct __attribute__ ((packed)) CmdComplete {
       uint8_t ncmd;
       uint16_t opcode;
       uint8_t status;
     } *cmdCompleteHeader = (CmdComplete*)&pdata[sizeof(HCIEventHdr)];
-
+#ifdef _BLE_TRACE_
+    Serial.print("E ncmd:   0x");
+    Serial.println(cmdCompleteHeader->ncmd,HEX);
+    Serial.print("E opcode: 0x");
+    Serial.println(cmdCompleteHeader->opcode, HEX);
+    Serial.print("E status: 0x");
+    Serial.println(cmdCompleteHeader->status, HEX);
+#endif
     _cmdCompleteOpcode = cmdCompleteHeader->opcode;
     _cmdCompleteStatus = cmdCompleteHeader->status;
     _cmdResponseLen = pdata[1] - sizeof(CmdComplete);
     _cmdResponse = &pdata[sizeof(HCIEventHdr) + sizeof(CmdComplete)];
 
-  } else if (eventHdr->evt == EVT_CMD_STATUS) {
+  }
+  else if (eventHdr->evt == EVT_CMD_STATUS)
+  {
     struct __attribute__ ((packed)) CmdStatus {
       uint8_t status;
       uint8_t ncmd;
       uint16_t opcode;
     } *cmdStatusHeader = (CmdStatus*)&pdata[sizeof(HCIEventHdr)];
 
+#ifdef _BLE_TRACE_
+    Serial.print("F n cmd:  0x");
+    Serial.println(cmdStatusHeader->ncmd, HEX);
+    Serial.print("F status: 0x");
+    Serial.println(cmdStatusHeader->status, HEX);
+    Serial.print("F opcode: 0x");
+    Serial.println(cmdStatusHeader->opcode, HEX);
+#endif
     _cmdCompleteOpcode = cmdStatusHeader->opcode;
     _cmdCompleteStatus = cmdStatusHeader->status;
     _cmdResponseLen = 0;
-  } else if (eventHdr->evt == EVT_NUM_COMP_PKTS) {
+  }
+  else if (eventHdr->evt == EVT_NUM_COMP_PKTS)
+  {
     uint8_t numHandles = pdata[sizeof(HCIEventHdr)];
     uint16_t* data = (uint16_t*)&pdata[sizeof(HCIEventHdr) + sizeof(numHandles)];
 
     for (uint8_t i = 0; i < numHandles; i++) {
       handleNumCompPkts(data[0], data[1]);
-
+#ifdef _BLE_TRACE_
+      Serial.print("Outstanding packets: ");
+      Serial.println(_pendingPkt);
+      Serial.print("Data[0]: 0x");
+      Serial.println(data[0]);
+      Serial.print("Data[1]: 0x");
+      Serial.println(data[1]);
+#endif
       data += 2;
     }
-  } else if (eventHdr->evt == EVT_LE_META_EVENT) {
+  }
+  else if(eventHdr->evt == 0x10)
+  {
+#ifdef _BLE_TRACE_
+    struct __attribute__ ((packed)) CmdHardwareError {
+      uint8_t hardwareCode;
+    } *cmdHardwareError = (CmdHardwareError*)&pdata[sizeof(HCIEventHdr)];
+    Serial.print("Bluetooth hardware error.");
+    Serial.print(" Code: 0x");
+    Serial.println(cmdHardwareError->hardwareCode, HEX);
+#endif
+  }
+  else if (eventHdr->evt == EVT_LE_META_EVENT)
+  {
     struct __attribute__ ((packed)) LeMetaEventHeader {
       uint8_t subevent;
     } *leMetaHeader = (LeMetaEventHeader*)&pdata[sizeof(HCIEventHdr)];
+#ifdef _BLE_TRACE_
+    Serial.print("\tSubEvent: 0x");
+    Serial.println(leMetaHeader->subevent,HEX);
+#endif
+    switch((LE_META_EVENT)leMetaHeader->subevent){
+      case 0x0A:{
+        struct __attribute__ ((packed)) EvtLeConnectionComplete {
+          uint8_t status;
+          uint16_t handle;
+          uint8_t role;
+          uint8_t peerBdaddrType;
+          uint8_t peerBdaddr[6];
+          uint8_t localResolvablePrivateAddress[6];
+          uint8_t peerResolvablePrivateAddress[6];
+          uint16_t interval;
+          uint16_t latency;
+          uint16_t supervisionTimeout;
+          uint8_t masterClockAccuracy;
+        } *leConnectionComplete = (EvtLeConnectionComplete*)&pdata[sizeof(HCIEventHdr) + sizeof(LeMetaEventHeader)];
+      
+        if (leConnectionComplete->status == 0x00) {
+          ATT.addConnection(leConnectionComplete->handle,
+                            leConnectionComplete->role,
+                            leConnectionComplete->peerBdaddrType,
+                            leConnectionComplete->peerBdaddr,
+                            leConnectionComplete->interval,
+                            leConnectionComplete->latency,
+                            leConnectionComplete->supervisionTimeout,
+                            leConnectionComplete->masterClockAccuracy);
+
+          L2CAPSignaling.addConnection(leConnectionComplete->handle,
+                                leConnectionComplete->role,
+                                leConnectionComplete->peerBdaddrType,
+                                leConnectionComplete->peerBdaddr,
+                                leConnectionComplete->interval,
+                                leConnectionComplete->latency,
+                                leConnectionComplete->supervisionTimeout,
+                                leConnectionComplete->masterClockAccuracy);
+        }
+        // uint8_t address[6];
+        // uint8_t BDAddr[6];
+        // for(int i=0; i<6; i++) BDAddr[5-i] = leConnectionComplete->peerBdaddr[i];
+        // leReadPeerResolvableAddress(leConnectionComplete->peerBdaddrType,BDAddr,address);
+        // Serial.print("Resolving address: ");
+        // btct.printBytes(BDAddr, 6);
+        // Serial.print("BT answer         : ");
+        // btct.printBytes(address, 6);
+
+#ifdef _BLE_TRACE_
+        Serial.print("Resolved peer     : ");
+        btct.printBytes(leConnectionComplete->peerResolvablePrivateAddress,6);
+        Serial.print("Resolved local    : ");
+        btct.printBytes(leConnectionComplete->localResolvablePrivateAddress,6);
+#endif
+        break;
+      }
+      case CONN_COMPLETE:{
+        struct __attribute__ ((packed)) EvtLeConnectionComplete {
+          uint8_t status;
+          uint16_t handle;
+          uint8_t role;
+          uint8_t peerBdaddrType;
+          uint8_t peerBdaddr[6];
+          uint16_t interval;
+          uint16_t latency;
+          uint16_t supervisionTimeout;
+          uint8_t masterClockAccuracy;
+        } *leConnectionComplete = (EvtLeConnectionComplete*)&pdata[sizeof(HCIEventHdr) + sizeof(LeMetaEventHeader)];
+      
+        if (leConnectionComplete->status == 0x00) {
+          ATT.addConnection(leConnectionComplete->handle,
+                            leConnectionComplete->role,
+                            leConnectionComplete->peerBdaddrType,
+                            leConnectionComplete->peerBdaddr,
+                            leConnectionComplete->interval,
+                            leConnectionComplete->latency,
+                            leConnectionComplete->supervisionTimeout,
+                            leConnectionComplete->masterClockAccuracy);
+
+          L2CAPSignaling.addConnection(leConnectionComplete->handle,
+                                leConnectionComplete->role,
+                                leConnectionComplete->peerBdaddrType,
+                                leConnectionComplete->peerBdaddr,
+                                leConnectionComplete->interval,
+                                leConnectionComplete->latency,
+                                leConnectionComplete->supervisionTimeout,
+                                leConnectionComplete->masterClockAccuracy);
+        }
+        // leReadPeerResolvableAddress(leConnectionComplete->peerBdaddrType,BDAddr,address);
+        // Serial.print("Resolving address: ");
+        // btct.printBytes(BDAddr, 6);
+        // Serial.print("BT answer         : ");
+        // btct.printBytes(address, 6);
+        break;
+      }
+      case ADVERTISING_REPORT:{
+        struct __attribute__ ((packed)) EvtLeAdvertisingReport {
+          uint8_t status;
+          uint8_t type;
+          uint8_t peerBdaddrType;
+          uint8_t peerBdaddr[6];
+          uint8_t eirLength;
+          uint8_t eirData[31];
+        } *leAdvertisingReport = (EvtLeAdvertisingReport*)&pdata[sizeof(HCIEventHdr) + sizeof(LeMetaEventHeader)];
+
+        if(leAdvertisingReport->eirLength > sizeof(leAdvertisingReport->eirData)){
+          return ;
+        }
 
-    if (leMetaHeader->subevent == EVT_LE_CONN_COMPLETE) {
-      struct __attribute__ ((packed)) EvtLeConnectionComplete {
-        uint8_t status;
-        uint16_t handle;
-        uint8_t role;
-        uint8_t peerBdaddrType;
-        uint8_t peerBdaddr[6];
-        uint16_t interval;
-        uint16_t latency;
-        uint16_t supervisionTimeout;
-        uint8_t masterClockAccuracy;
-      } *leConnectionComplete = (EvtLeConnectionComplete*)&pdata[sizeof(HCIEventHdr) + sizeof(LeMetaEventHeader)];
-    
-      if (leConnectionComplete->status == 0x00) {
-        ATT.addConnection(leConnectionComplete->handle,
-                          leConnectionComplete->role,
-                          leConnectionComplete->peerBdaddrType,
-                          leConnectionComplete->peerBdaddr,
-                          leConnectionComplete->interval,
-                          leConnectionComplete->latency,
-                          leConnectionComplete->supervisionTimeout,
-                          leConnectionComplete->masterClockAccuracy);
-
-        L2CAPSignaling.addConnection(leConnectionComplete->handle,
-                              leConnectionComplete->role,
-                              leConnectionComplete->peerBdaddrType,
-                              leConnectionComplete->peerBdaddr,
-                              leConnectionComplete->interval,
-                              leConnectionComplete->latency,
-                              leConnectionComplete->supervisionTimeout,
-                              leConnectionComplete->masterClockAccuracy);
+        if (leAdvertisingReport->status == 0x01) {
+          // last byte is RSSI
+          int8_t rssi = leAdvertisingReport->eirData[leAdvertisingReport->eirLength];
+
+          GAP.handleLeAdvertisingReport(leAdvertisingReport->type,
+                                        leAdvertisingReport->peerBdaddrType,
+                                        leAdvertisingReport->peerBdaddr,
+                                        leAdvertisingReport->eirLength,
+                                        leAdvertisingReport->eirData,
+                                        rssi);
+        }
+        break;
       }
-    } else if (leMetaHeader->subevent == EVT_LE_ADVERTISING_REPORT) {
-      struct __attribute__ ((packed)) EvtLeAdvertisingReport {
-        uint8_t status;
-        uint8_t type;
-        uint8_t peerBdaddrType;
-        uint8_t peerBdaddr[6];
-        uint8_t eirLength;
-        uint8_t eirData[31];
-      } *leAdvertisingReport = (EvtLeAdvertisingReport*)&pdata[sizeof(HCIEventHdr) + sizeof(LeMetaEventHeader)];
-
-      if (leAdvertisingReport->status == 0x01) {
-        // last byte is RSSI
-        int8_t rssi = leAdvertisingReport->eirData[leAdvertisingReport->eirLength];
-
-        GAP.handleLeAdvertisingReport(leAdvertisingReport->type,
-                                      leAdvertisingReport->peerBdaddrType,
-                                      leAdvertisingReport->peerBdaddr,
-                                      leAdvertisingReport->eirLength,
-                                      leAdvertisingReport->eirData,
-                                      rssi);
+      case LONG_TERM_KEY_REQUEST:{
+        struct __attribute__ ((packed)) LTKRequest
+        {
+          uint8_t subEventCode;
+          uint16_t connectionHandle;
+          uint8_t randomNumber[8];
+          uint8_t encryptedDiversifier[2];
+        } *ltkRequest = (LTKRequest*)&pdata[sizeof(HCIEventHdr)];
+#ifdef _BLE_TRACE_
+        Serial.println("LTK request received");
+        Serial.print("Connection Handle: ");
+        btct.printBytes((uint8_t*)&ltkRequest->connectionHandle,2);
+        Serial.print("Random Number    : ");
+        btct.printBytes(ltkRequest->randomNumber,8);
+        Serial.print("EDIV           : ");
+        btct.printBytes(ltkRequest->encryptedDiversifier,2);
+#endif
+        // Load our LTK for this connection.
+        uint8_t peerAddr[7];
+        uint8_t resolvableAddr[6];
+        uint8_t foundLTK;
+        ATT.getPeerAddrWithType(ltkRequest->connectionHandle, peerAddr);
+        
+        if((ATT.getPeerEncryption(ltkRequest->connectionHandle) & PEER_ENCRYPTION::PAIRING_REQUEST)>0){
+          // Pairing request - LTK is one in buffer already
+          foundLTK = 1;
+        }else{
+          if(ATT.getPeerResolvedAddress(ltkRequest->connectionHandle, resolvableAddr)){
+            foundLTK = getLTK(resolvableAddr, HCI.LTK);
+          }else{
+            foundLTK = getLTK(&peerAddr[1], HCI.LTK);
+          }
+        }
+        // } //2d
+        // Send our LTK back
+        if(foundLTK){
+          struct __attribute__ ((packed)) LTKReply
+          {
+            uint16_t connectionHandle;
+            uint8_t LTK[16];
+          } ltkReply = {0,0};
+          ltkReply.connectionHandle = ltkRequest->connectionHandle;
+          for(int i=0; i<16; i++) ltkReply.LTK[15-i] = HCI.LTK[i];
+          int result = sendCommand(OGF_LE_CTL << 10 | LE_COMMAND::LONG_TERM_KEY_REPLY,sizeof(ltkReply), &ltkReply);
+
+  #ifdef _BLE_TRACE_
+          Serial.println("Sending LTK as: ");
+          btct.printBytes(ltkReply.LTK,16);
+  #endif
+
+          if(result == 0){
+            struct __attribute__ ((packed)) LTKReplyResult
+            {
+              uint8_t status;
+              uint16_t connectionHandle;
+            } ltkReplyResult = {0,0};
+            memcpy(&ltkReplyResult, _cmdResponse, 3);
+
+  #ifdef _BLE_TRACE_
+            Serial.println("LTK send success");
+            Serial.print("status     : ");
+            btct.printBytes(&ltkReplyResult.status,1);
+            Serial.print("Conn Handle: ");
+            btct.printBytes((uint8_t*)&ltkReplyResult.connectionHandle,2);
+  #endif
+          }else{
+  #ifdef _BLE_TRACE_
+            Serial.print("Failed to send LTK...: ");
+            btct.printBytes((uint8_t*)&result,2);
+  #endif
+          }
+        }else{
+          /// do LTK rejection
+#ifdef _BLE_TRACE_
+          Serial.println("LTK not found, rejecting");
+#endif
+          sendCommand(OGF_LE_CTL << 10 | LE_COMMAND::LONG_TERM_KEY_NEGATIVE_REPLY,2, &ltkRequest->connectionHandle);
+        }
+        break;
+      }
+      case REMOTE_CONN_PARAM_REQ:{
+        struct __attribute__ ((packed)) RemoteConnParamReq {
+          uint8_t subEventCode;
+          uint16_t connectionHandle;
+          uint16_t intervalMin;
+          uint16_t intervalMax;
+          uint16_t latency;
+          uint16_t timeOut;
+        } *remoteConnParamReq = (RemoteConnParamReq*)&pdata[sizeof(HCIEventHdr)];
+#ifdef _BLE_TRACE_
+        Serial.println("--- Remtoe conn param req");
+        Serial.print("Handle      : ");
+        btct.printBytes((uint8_t*)&remoteConnParamReq->connectionHandle,2);
+        Serial.print("Interval min: ");
+        btct.printBytes((uint8_t*)&remoteConnParamReq->intervalMin,2);
+        Serial.print("Interval max: ");
+        btct.printBytes((uint8_t*)&remoteConnParamReq->intervalMax,2);
+        Serial.print("Latency     : ");
+        btct.printBytes((uint8_t*)&remoteConnParamReq->latency,2);
+        Serial.print("Timeout     : ");
+        btct.printBytes((uint8_t*)&remoteConnParamReq->timeOut,2);
+#endif
+        
+        struct __attribute__ ((packed)) RemoteConnParamReqReply {
+          uint16_t connectionHandle;
+          uint16_t intervalMin;
+          uint16_t intervalMax;
+          uint16_t latency;
+          uint16_t timeOut;
+          uint16_t minLength;
+          uint16_t maxLength;
+        } remoteConnParamReqReply;
+        memcpy(&remoteConnParamReqReply, &remoteConnParamReq->connectionHandle, sizeof(RemoteConnParamReq)-1);
+
+        remoteConnParamReqReply.minLength = 0x000F;
+        remoteConnParamReqReply.maxLength = 0x0FFF;
+        sendCommand(OGF_LE_CTL << 10 | 0x20, sizeof(RemoteConnParamReqReply), &remoteConnParamReqReply);
+        break;
+      }
+      case READ_LOCAL_P256_COMPLETE:{
+        struct __attribute__ ((packed)) EvtReadLocalP256Complete{
+          uint8_t subEventCode;
+          uint8_t status;
+          uint8_t localPublicKey[64];
+        } *evtReadLocalP256Complete = (EvtReadLocalP256Complete*)&pdata[sizeof(HCIEventHdr)];
+        if(evtReadLocalP256Complete->status == 0x0){
+#ifdef _BLE_TRACE_
+          Serial.println("Key read success");
+#endif
+          struct __attribute__ ((packed)) PairingPublicKey
+          {
+            uint8_t code;
+            uint8_t publicKey[64];
+          } pairingPublicKey = {CONNECTION_PAIRING_PUBLIC_KEY,0};
+          memcpy(pairingPublicKey.publicKey,evtReadLocalP256Complete->localPublicKey,64);
+          memcpy(localPublicKeyBuffer,      evtReadLocalP256Complete->localPublicKey,64);
+
+          // Send the local public key to the remote
+          uint16_t connectionHandle = ATT.getPeerEncrptingConnectionHandle();
+          if(connectionHandle==ATT_MAX_PEERS+1){
+#ifdef _BLE_TRACE_
+            Serial.println("failed to find connection handle");
+#endif
+            break;
+          }
+          HCI.sendAclPkt(connectionHandle,SECURITY_CID,sizeof(PairingPublicKey),&pairingPublicKey);
+          uint8_t encryption = ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::SENT_PUBKEY;
+          ATT.setPeerEncryption(connectionHandle, encryption);
+          
+
+          uint8_t Z = 0;
+          
+          HCI.leRand(Nb);
+          HCI.leRand(&Nb[8]);
+          
+#ifdef _BLE_TRACE_
+          Serial.print("nb: ");
+          btct.printBytes(Nb, 16);
+#endif
+          struct __attribute__ ((packed)) F4Params
+          {
+            uint8_t U[32];
+            uint8_t V[32];
+            uint8_t Z;
+          } f4Params = {0,0,Z};
+          for(int i=0; i<32; i++){
+            f4Params.U[31-i] = pairingPublicKey.publicKey[i]; 
+            f4Params.V[31-i] = HCI.remotePublicKeyBuffer[i];
+          }
+          
+          struct __attribute__ ((packed)) PairingConfirm
+          {
+            uint8_t code;
+            uint8_t cb[16];
+          } pairingConfirm = {CONNECTION_PAIRING_CONFIRM,0};
+          
+          btct.AES_CMAC(Nb,(unsigned char *)&f4Params,sizeof(f4Params),pairingConfirm.cb);
+
+#ifdef _BLE_TRACE_
+          Serial.print("cb: ");
+          btct.printBytes(pairingConfirm.cb, 16);
+#endif
 
+          uint8_t cb_temp[sizeof(pairingConfirm.cb)];
+          for(int i=0; i<sizeof(pairingConfirm.cb);i++){
+            cb_temp[sizeof(pairingConfirm.cb)-1-i] = pairingConfirm.cb[i];
+          }
+          /// cb wa back to front.
+          memcpy(pairingConfirm.cb,cb_temp,sizeof(pairingConfirm.cb));
+
+          // Send Pairing confirm response
+          HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(pairingConfirm), &pairingConfirm);
+          
+          HCI.sendCommand( (OGF_LE_CTL << 10) | LE_COMMAND::GENERATE_DH_KEY_V1, sizeof(HCI.remotePublicKeyBuffer), HCI.remotePublicKeyBuffer);
+        }else{
+#ifdef _BLE_TRACE_
+          Serial.print("Key read error: 0x");
+          Serial.println(evtReadLocalP256Complete->status,HEX);
+          for(int i=0; i<64; i++){
+            Serial.print(" 0x");
+            Serial.print(evtReadLocalP256Complete->localPublicKey[i],HEX);
+          }
+          Serial.println(".");
+#endif
+        }
+        break;
       }
+      case GENERATE_DH_KEY_COMPLETE:{
+        struct __attribute__ ((packed)) EvtLeDHKeyComplete{
+          uint8_t subEventCode;
+          uint8_t status;
+          uint8_t DHKey[32];
+        } *evtLeDHKeyComplete = (EvtLeDHKeyComplete*)&pdata[sizeof(HCIEventHdr)];
+        if(evtLeDHKeyComplete->status == 0x0){
+#ifdef _BLE_TRACE_
+          Serial.println("DH key generated");
+#endif
+          uint16_t connectionHandle = ATT.getPeerEncrptingConnectionHandle();
+          if(connectionHandle==ATT_MAX_PEERS+1){
+#ifdef _BLE_TRACE_
+            Serial.println("Failed to find connection handle DH key check");
+#endif
+            break;
+          }
+          
+          
+          for(int i=0; i<32; i++) DHKey[31-i] = evtLeDHKeyComplete->DHKey[i];
+
+#ifdef _BLE_TRACE_
+          Serial.println("Stored our DHKey:");
+          btct.printBytes(DHKey,32);
+#endif
+          uint8_t encryption = ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::DH_KEY_CALULATED;
+          ATT.setPeerEncryption(connectionHandle, encryption);
+
+          if((encryption & PEER_ENCRYPTION::RECEIVED_DH_CHECK) > 0){
+#ifdef _BLE_TRACE_
+            Serial.println("Received DHKey check already so calculate f5, f6 now.");
+#endif
+            L2CAPSignaling.smCalculateLTKandConfirm(connectionHandle, HCI.remoteDHKeyCheckBuffer);
+	  
+          }else{
+#ifdef _BLE_TRACE_
+            Serial.println("Waiting on other DHKey check before calculating.");
+#endif
+          }
+        }else{
+#ifdef _BLE_TRACE_
+          Serial.print("Key generation error: 0x");
+          Serial.println(evtLeDHKeyComplete->status, HEX);
+#endif
+        }
+        break;
+      }
+      default:
+      {
+#ifdef _BLE_TRACE_
+        Serial.println("[Info] Unhandled meta event");
+#endif
+      }
+    }
+  }else{
+#ifdef _BLE_TRACE_
+    Serial.println("[Info] Unhandled event");
+#endif
+  }
+}
+int HCIClass::leEncrypt(uint8_t* key, uint8_t* plaintext, uint8_t* status, uint8_t* ciphertext){
+  struct __attribute__ ((packed)) LeEncryptCommand
+  {
+    uint8_t key[16];
+    uint8_t plaintext[16];
+  } leEncryptCommand = {0,0};
+  for(int i=0; i<16; i++){
+    leEncryptCommand.key[15-i] = key[i];
+    leEncryptCommand.plaintext[15-i] = plaintext[i];
+  }
+  
+  int res = sendCommand(OGF_LE_CTL << 10 | LE_COMMAND::ENCRYPT, 32, &leEncryptCommand);
+  if(res == 0){
+#ifdef _BLE_TRACE_
+    Serial.print("Copying from command Response length: ");
+    Serial.println(_cmdResponseLen);
+    Serial.println(".");
+    for(int i=0; i<20; i++){
+        Serial.print(" 0x");
+        Serial.print(_cmdResponse[i],HEX);
     }
+    Serial.println(".");
+#endif
+    for(int i=0; i<16; i++){
+      ciphertext[15-i] = _cmdResponse[i];
+    }
+    return 1;
+  }
+#ifdef _BLE_TRACE_
+  Serial.print("Error with AES: 0x");
+  Serial.println(res, HEX);
+#endif
+  return res;
+}
+int HCIClass::leRand(uint8_t rand[]){
+  int res = sendCommand(OGF_LE_CTL << 10 | LE_COMMAND::RANDOM);
+  if(res == 0){
+    memcpy(rand,_cmdResponse, 8); /// backwards but it's a random number
+  }
+  return res;
+}
+int HCIClass::getLTK(uint8_t* address, uint8_t* LTK){
+  if(_getLTK!=0){
+    return _getLTK(address, LTK);
+  }else{
+    return 0;
+  }
+}
+int HCIClass::storeIRK(uint8_t* address, uint8_t* IRK){
+  if(_storeIRK!=0){
+    return _storeIRK(address, IRK);
+  }else{
+    return 0;
+  }
+}
+int HCIClass::storeLTK(uint8_t* address, uint8_t* LTK){
+  if(_storeLTK!=0){
+    return _storeLTK(address, LTK);
+  }else{
+    return 0;
+  }
+}
+uint8_t HCIClass::localIOCap(){
+  if(_displayCode!=0){
+    /// We have a display
+    if(_binaryConfirmPairing!=0){
+      return IOCAP_DISPLAY_YES_NO;
+    }else{
+      return IOCAP_DISPLAY_ONLY;
+    }
+  }else{
+    // We have no display
+    return IOCAP_NO_INPUT_NO_OUTPUT;
+  }
+}
+
+/// Stub function to generate parameters for local authreq
+AuthReq HCIClass::localAuthreq(){
+  // If get, set, IRK, LTK all set then we can bond.
+  AuthReq local = AuthReq();
+  if(_storeIRK!=0 && _storeLTK!=0 && _getLTK!=0 && _getIRKs!=0){
+    local.setBonding(true);
   }
+  local.setSC(true);
+  local.setMITM(true);
+  local.setCT2(true);
+  return LOCAL_AUTHREQ;
 }
 
 void HCIClass::dumpPkt(const char* prefix, uint8_t plen, uint8_t pdata[])
diff --git a/src/utility/HCI.h b/src/utility/HCI.h
index b4a40e63..75e9ff99 100644
--- a/src/utility/HCI.h
+++ b/src/utility/HCI.h
@@ -21,7 +21,35 @@
 #define _HCI_H_
 
 #include <Arduino.h>
+#include "bitDescriptions.h"
 #include "HCITransport.h"
+#include "L2CAPSignaling.h"
+
+#define OGF_LINK_CTL           0x01
+#define OGF_HOST_CTL           0x03
+#define OGF_INFO_PARAM         0x04
+#define OGF_STATUS_PARAM       0x05
+#define OGF_LE_CTL             0x08
+
+enum LE_COMMAND {
+  ENCRYPT                      = 0x0017,
+  RANDOM                       = 0x0018,
+  LONG_TERM_KEY_REPLY          = 0x001A,
+  LONG_TERM_KEY_NEGATIVE_REPLY = 0x001B,
+  READ_LOCAL_P256              = 0x0025,
+  GENERATE_DH_KEY_V1           = 0x0026,
+  GENERATE_DH_KEY_V2           = 0x005E
+};
+enum LE_META_EVENT {
+  CONN_COMPLETE             = 0x01,
+  ADVERTISING_REPORT        = 0x02,
+  LONG_TERM_KEY_REQUEST     = 0x05,
+  REMOTE_CONN_PARAM_REQ     = 0x06,
+  READ_LOCAL_P256_COMPLETE  = 0x08,
+  GENERATE_DH_KEY_COMPLETE  = 0x09
+};
+String metaEventToString(LE_META_EVENT event);
+String commandToString(LE_COMMAND command);
 
 class HCIClass {
 public:
@@ -37,12 +65,14 @@ class HCIClass {
   virtual int reset();
   virtual int readLocalVersion(uint8_t& hciVer, uint16_t& hciRev, uint8_t& lmpVer,
                        uint16_t& manufacturer, uint16_t& lmpSubVer);
+
   virtual int readBdAddr(uint8_t addr[6]);
+  virtual int readBdAddr();
 
   virtual int readRssi(uint16_t handle);
 
   virtual int setEventMask(uint64_t eventMask);
-
+  virtual int setLeEventMask(uint64_t leEventMask);
   virtual int readLeBufferSize(uint16_t& pktLen, uint8_t& maxPkt);
   virtual int leSetRandomAddress(uint8_t addr[6]);
   virtual int leSetAdvertisingParameters(uint16_t minInterval, uint16_t maxInterval,
@@ -63,20 +93,54 @@ class HCIClass {
   virtual int leConnUpdate(uint16_t handle, uint16_t minInterval, uint16_t maxInterval, 
                   uint16_t latency, uint16_t supervisionTimeout);
   virtual int leCancelConn();
-  virtual int leRand(uint8_t randomNumber[8]);
+  virtual int leEncrypt(uint8_t* Key, uint8_t* plaintext, uint8_t* status, uint8_t* ciphertext);
+  // Generate a 64 bit random number
+  virtual int leRand(uint8_t rand[]);
+  virtual AuthReq localAuthreq();
+  virtual uint8_t localIOCap();
+
+  virtual void saveNewAddress(uint8_t addressType, uint8_t* address, uint8_t* peerIrk, uint8_t* remoteIrk);
+  virtual void leAddResolvingAddress(uint8_t addressType, uint8_t* address, uint8_t* peerIrk, uint8_t* remoteIrk);
+  virtual int leStopResolvingAddresses();
+  virtual int leStartResolvingAddresses();
+  virtual int leReadPeerResolvableAddress(uint8_t peerAddressType, uint8_t* peerIdentityAddress, uint8_t* peerResolvableAddress);
 
+  virtual void readStoredLKs();
+  virtual int readStoredLK(uint8_t BD_ADDR[], uint8_t read_all = 0);
+  virtual void writeLK(uint8_t peerAddress[], uint8_t LK[]);
+  virtual int tryResolveAddress(uint8_t* BDAddr, uint8_t* address);
 
-  virtual int sendAclPkt(uint16_t handle, uint8_t cid, uint8_t plen, void* data);
+  virtual int sendAclPkt(uint16_t handle, uint8_t cid, uint8_t plen, const void* data);
 
   virtual int disconnect(uint16_t handle);
 
   virtual void debug(Stream& stream);
   virtual void noDebug();
 
+  // TODO: Send command be private again & use ATT implementation of send command within ATT.
   void setTransport(HCITransportInterface *HCITransport);
+  virtual int getLTK(uint8_t *address, uint8_t *LTK);
+  virtual int storeLTK(uint8_t *address, uint8_t *LTK);
+  virtual int storeIRK(uint8_t *address, uint8_t *IRK);
+  int (*_storeIRK)(uint8_t *address, uint8_t *peerIrk) = 0;
+  int (*_getIRKs)(uint8_t *nIRKs, uint8_t **BADDR_type, uint8_t ***BADDRs, uint8_t ***IRKs) = 0;
+  int (*_storeLTK)(uint8_t *, uint8_t *) = 0;
+  int (*_getLTK)(uint8_t *, uint8_t *) = 0;
+  void (*_displayCode)(uint32_t confirmationCode) = 0;
+  bool (*_binaryConfirmPairing)() = 0;
+
+  virtual int sendCommand(uint16_t opcode, uint8_t plen = 0, const void* parameters = nullptr);
+  uint8_t remotePublicKeyBuffer[64];
+  uint8_t localPublicKeyBuffer[64];
+  uint8_t remoteDHKeyCheckBuffer[16];
+  uint8_t Na[16];
+  uint8_t Nb[16];
+  uint8_t DHKey[32];
+  uint8_t localAddr[6];
+  uint8_t LTK[16];
+  
 
 private:
-  virtual int sendCommand(uint16_t opcode, uint8_t plen = 0, void* parameters = NULL);
 
   virtual void handleAclDataPkt(uint8_t plen, uint8_t pdata[]);
   virtual void handleNumCompPkts(uint16_t handle, uint16_t numPkts);
diff --git a/src/utility/L2CAPSignaling.cpp b/src/utility/L2CAPSignaling.cpp
index 3cca56cc..fbdcd5cd 100644
--- a/src/utility/L2CAPSignaling.cpp
+++ b/src/utility/L2CAPSignaling.cpp
@@ -18,16 +18,21 @@
 */
 
 #include "HCI.h"
-
+#include "ATT.h"
+#include "btct.h"
 #include "L2CAPSignaling.h"
-
+#include "keyDistribution.h"
+#include "bitDescriptions.h"
 #define CONNECTION_PARAMETER_UPDATE_REQUEST  0x12
 #define CONNECTION_PARAMETER_UPDATE_RESPONSE 0x13
 
+//#define _BLE_TRACE_
+
 L2CAPSignalingClass::L2CAPSignalingClass() :
   _minInterval(0),
   _maxInterval(0),
-  _supervisionTimeout(0)
+  _supervisionTimeout(0),
+  _pairing_enabled(1)
 {
 }
 
@@ -108,6 +113,327 @@ void L2CAPSignalingClass::handleData(uint16_t connectionHandle, uint8_t dlen, ui
     connectionParameterUpdateResponse(connectionHandle, identifier, length, data);
   }
 }
+void L2CAPSignalingClass::handleSecurityData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[])
+{
+  struct __attribute__ ((packed)) L2CAPSignalingHdr {
+    uint8_t code;
+    uint8_t data[64];
+  } *l2capSignalingHdr = (L2CAPSignalingHdr*)data;
+#ifdef _BLE_TRACE_
+  Serial.print("dlen: ");
+  Serial.println(dlen);
+#endif
+  uint8_t code = l2capSignalingHdr->code;
+
+#ifdef _BLE_TRACE_
+  Serial.print("handleSecurityData: code: 0x");
+  Serial.println(code, HEX);
+  Serial.print("rx security:");
+  btct.printBytes(data,dlen);
+#endif
+  if (code == CONNECTION_PAIRING_REQUEST) {
+	  
+    if (isPairingEnabled()){
+      if (_pairing_enabled >= 2) _pairing_enabled = 0;  // 2 = pair once only
+      
+      // 0x1
+      struct __attribute__ ((packed)) PairingRequest {
+        uint8_t ioCapability;
+        uint8_t oobDataFlag;
+        uint8_t authReq;
+        uint8_t maxEncSize;
+        uint8_t initiatorKeyDistribution;
+        uint8_t responderKeyDistribution;
+      } *pairingRequest = (PairingRequest*)l2capSignalingHdr->data;
+
+      KeyDistribution responseKD = KeyDistribution();
+      responseKD.setIdKey(true);
+
+      ATT.remoteKeyDistribution = responseKD;// KeyDistribution(pairingRequest->initiatorKeyDistribution);
+      ATT.localKeyDistribution = responseKD; //KeyDistribution(pairingRequest->responderKeyDistribution);
+      // KeyDistribution rkd(pairingRequest->responderKeyDistribution);
+      AuthReq req(pairingRequest->authReq);
+#ifdef _BLE_TRACE_
+      Serial.print("Req has properties: ");
+      Serial.print(req.Bonding()?"bonding, ":"no bonding, ");
+      Serial.print(req.CT2()?"CT2, ":"no CT2, ");
+      Serial.print(req.KeyPress()?"KeyPress, ":"no KeyPress, ");
+      Serial.print(req.MITM()?"MITM, ":"no MITM, ");
+      Serial.print(req.SC()?"SC, ":"no SC, ");
+#endif
+    
+      uint8_t peerIOCap[3];
+      peerIOCap[0] = pairingRequest->authReq;
+      peerIOCap[1] = pairingRequest->oobDataFlag;
+      peerIOCap[2] = pairingRequest->ioCapability;
+      ATT.setPeerIOCap(connectionHandle, peerIOCap);
+      ATT.setPeerEncryption(connectionHandle, ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::PAIRING_REQUEST);
+#ifdef _BLE_TRACE_
+      Serial.print("Peer encryption : 0b");
+      Serial.println(ATT.getPeerEncryption(connectionHandle), BIN);
+#endif
+      struct __attribute__ ((packed)) PairingResponse {
+        uint8_t code;
+        uint8_t ioCapability;
+        uint8_t oobDataFlag;
+        uint8_t authReq;
+        uint8_t maxEncSize;
+        uint8_t initiatorKeyDistribution;
+        uint8_t responderKeyDistribution;
+      } response = { CONNECTION_PAIRING_RESPONSE, HCI.localIOCap(), 0, HCI.localAuthreq().getOctet(), 0x10, responseKD.getOctet(), responseKD.getOctet()};
+     
+      HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(response), &response);
+      
+    } else {
+      // Pairing not enabled
+      uint8_t ret[2] = {CONNECTION_PAIRING_FAILED, 0x05}; // reqect pairing
+      HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(ret), ret);
+      ATT.setPeerEncryption(connectionHandle, NO_ENCRYPTION);
+    }
+  }
+  else if (code == CONNECTION_PAIRING_RANDOM)
+  {
+    struct __attribute__ ((packed)) PairingRandom {
+      uint8_t Na[16];
+    } *pairingRandom = (PairingRandom*)l2capSignalingHdr->data;
+    for(int i=0; i<16; i++){
+      HCI.Na[15-i] = pairingRandom->Na[i];
+    }
+#ifdef _BLE_TRACE_
+    Serial.println("[Info] Pairing random.");
+#endif
+    struct __attribute__ ((packed)) PairingResponse {
+      uint8_t code;
+      uint8_t Nb[16];
+    } response = { CONNECTION_PAIRING_RANDOM, 0};
+    for(int i=0; i< 16; i++) response.Nb[15-i] = HCI.Nb[i];
+
+    HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(response), &response);
+
+    // We now have all needed for compare value
+    uint8_t g2Result[4];
+    uint8_t U[32];
+    uint8_t V[32];
+    
+    for(int i=0; i<32; i++){
+      U[31-i] = HCI.remotePublicKeyBuffer[i];
+      V[31-i] = HCI.localPublicKeyBuffer[i];
+    }
+
+    btct.g2(U,V,HCI.Na,HCI.Nb, g2Result);
+    uint32_t result = 0;
+    for(int i=0; i<4; i++) result += g2Result[3-i] << 8*i;
+
+#ifdef _BLE_TRACE_
+    Serial.print("U      : ");
+    btct.printBytes(U,32);
+    Serial.print("V      : ");
+    btct.printBytes(V,32);
+    Serial.print("X      : ");
+    btct.printBytes(HCI.Na,16);
+    Serial.print("Y      : ");
+    btct.printBytes(HCI.Nb,16);
+    Serial.print("g2res  : ");
+    btct.printBytes(g2Result,4);
+    Serial.print("Result : ");
+    Serial.println(result);
+#endif
+
+    if(HCI._displayCode!=0){
+      HCI._displayCode(result%1000000);
+    }
+    if(HCI._binaryConfirmPairing!=0){
+      if(!HCI._binaryConfirmPairing()){
+#ifdef _BLE_TRACE_
+        Serial.println("User rejection");
+#endif
+        uint8_t rejection[2];
+        rejection[0] = CONNECTION_PAIRING_FAILED;
+        rejection[1] = 0x0C; // Numeric comparison failed
+        HCI.sendAclPkt(connectionHandle, SECURITY_CID, 2, rejection);
+        ATT.setPeerEncryption(connectionHandle, PEER_ENCRYPTION::NO_ENCRYPTION);
+      }else{
+#ifdef _BLE_TRACE_
+        Serial.println("User did confirm");
+#endif
+      }
+    }
+  }
+  else if (code == CONNECTION_PAIRING_RESPONSE)
+  {
+  }
+  else if(code == CONNECTION_PAIRING_FAILED)
+  {
+#ifdef _BLE_TRACE_
+    struct __attribute__ ((packed)) PairingFailed
+    {
+      uint8_t code;
+      uint8_t reason;
+    } *pairingFailed = (PairingFailed*)data;
+    Serial.print("Pairing failed with code: 0x");
+    Serial.println(pairingFailed->reason,HEX);
+#endif
+    ATT.setPeerEncryption(connectionHandle, PEER_ENCRYPTION::NO_ENCRYPTION);
+  }
+  else if (code == CONNECTION_IDENTITY_INFORMATION){
+    struct __attribute__ ((packed)) IdentityInformation {
+      uint8_t code;
+      uint8_t PeerIRK[16];
+    } *identityInformation = (IdentityInformation*)data;
+    for(int i=0; i<16; i++) ATT.peerIRK[15-i] = identityInformation->PeerIRK[i];
+#ifdef _BLE_TRACE_
+    Serial.println("Saved peer IRK");
+#endif
+  }
+  else if (code == CONNECTION_IDENTITY_ADDRESS){
+    struct __attribute__ ((packed)) IdentityAddress {
+      uint8_t code;
+      uint8_t addressType;
+      uint8_t address[6];
+    } *identityAddress = (IdentityAddress*)data;
+    // we can save this information now.
+    uint8_t peerAddress[6];
+    for(int i=0; i<6; i++) peerAddress[5-i] = identityAddress->address[i];
+
+    HCI.saveNewAddress(identityAddress->addressType, peerAddress, ATT.peerIRK, ATT.localIRK);
+    if(HCI._storeLTK!=0){
+      HCI._storeLTK(peerAddress, HCI.LTK);
+    }
+  }
+  else if (code == CONNECTION_PAIRING_PUBLIC_KEY){
+    /// Received a public key
+    struct __attribute__ ((packed)) ConnectionPairingPublicKey {
+      uint8_t x[32];
+      uint8_t y[32];
+    } *connectionPairingPublicKey = (ConnectionPairingPublicKey*)l2capSignalingHdr->data;
+    struct __attribute__ ((packed)) GenerateDHKeyCommand {
+      uint8_t x[32];
+      uint8_t y[32];
+    } generateDHKeyCommand = {
+      0x00,
+      0x00,
+    };
+    memcpy(generateDHKeyCommand.x,connectionPairingPublicKey->x,32);
+    memcpy(generateDHKeyCommand.y,connectionPairingPublicKey->y,32);
+
+    if(ATT.setPeerEncryption(connectionHandle, ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::REQUESTED_ENCRYPTION)){
+#ifdef _BLE_TRACE_
+      Serial.println("[Info] Pairing public key");
+      Serial.println("Requested encryption stored.");
+#endif
+    }else{
+#ifdef _BLE_TRACE_
+      Serial.println("[Info] Pairing public key");
+      Serial.print("Failed to store encryption request with handle: 0x");
+      Serial.println(connectionHandle,HEX);
+#endif
+    }
+    
+    memcpy(HCI.remotePublicKeyBuffer,&generateDHKeyCommand,sizeof(generateDHKeyCommand));
+    HCI.sendCommand( (OGF_LE_CTL << 10 )| LE_COMMAND::READ_LOCAL_P256, 0);
+  }
+  else if(code == CONNECTION_PAIRING_DHKEY_CHECK)
+  {
+    uint8_t RemoteDHKeyCheck[16];
+    for(int i=0; i<16; i++) RemoteDHKeyCheck[15-i] = l2capSignalingHdr->data[i];
+    
+
+#ifdef _BLE_TRACE_
+    Serial.println("[Info] DH Key check");
+    Serial.print("Remote DHKey Check: ");
+    btct.printBytes(RemoteDHKeyCheck, 16);
+#endif
+
+    
+    
+    uint8_t encryptionState = ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::RECEIVED_DH_CHECK;
+    ATT.setPeerEncryption(connectionHandle, encryptionState);
+    if((encryptionState & PEER_ENCRYPTION::DH_KEY_CALULATED) == 0){
+#ifdef _BLE_TRACE_
+      Serial.println("DHKey not yet ready, will calculate f5, f6 later");
+#endif
+      // store RemoteDHKeyCheck for later check
+      memcpy(HCI.remoteDHKeyCheckBuffer,RemoteDHKeyCheck,16);
+
+    } else {
+      // We've already calculated the DHKey so we can calculate our check and send it.
+      smCalculateLTKandConfirm(connectionHandle, RemoteDHKeyCheck);
+      
+    }
+  }
+}
+
+void L2CAPSignalingClass::smCalculateLTKandConfirm(uint16_t handle, uint8_t expectedEa[])
+{ // Authentication stage 2: LTK Calculation
+  
+  uint8_t localAddress[7];
+  uint8_t remoteAddress[7];
+  ATT.getPeerAddrWithType(handle, remoteAddress);
+  // @note Address is taken directly from HCI.localaddress, 
+  //  which is set when object DeviceLocal is created
+  //HCI.readBdAddr();
+  memcpy(&localAddress[1],HCI.localAddr,6);
+  localAddress[0] = ATT._ownBdaddrType; //@note Adding bit with address type (e.g. Static random or public address)
+
+  // Compute the LTK and MacKey
+  uint8_t MacKey[16];
+  btct.f5(HCI.DHKey, HCI.Na, HCI.Nb, remoteAddress, localAddress, MacKey, HCI.LTK);
+
+  // Compute Ea and Eb
+  uint8_t Ea[16];
+  uint8_t Eb[16];
+  uint8_t R[16];
+  uint8_t MasterIOCap[3];
+  uint8_t SlaveIOCap[3] = {HCI.localAuthreq().getOctet(), 0x0, HCI.localIOCap()};
+  
+  ATT.getPeerIOCap(handle, MasterIOCap);
+  for(int i=0; i<16; i++) R[i] = 0;
+  
+  btct.f6(MacKey, HCI.Na,HCI.Nb,R, MasterIOCap, remoteAddress, localAddress, Ea);
+  btct.f6(MacKey, HCI.Nb,HCI.Na,R, SlaveIOCap, localAddress, remoteAddress, Eb);
+
+#ifdef _BLE_TRACE_
+  Serial.println("Calculate and confirm LTK via f5, f6:");
+  Serial.print("DHKey      : ");  btct.printBytes(HCI.DHKey,32);
+  Serial.print("Na         : ");  btct.printBytes(HCI.Na,16);
+  Serial.print("Nb         : ");  btct.printBytes(HCI.Nb,16);
+  Serial.print("MacKey     : ");  btct.printBytes(MacKey,16);
+  Serial.print("LTK        : ");  btct.printBytes(HCI.LTK,16);
+  Serial.print("Expected Ea: ");  btct.printBytes(expectedEa, 16);
+  Serial.print("Ea         : ");  btct.printBytes(Ea, 16);
+  Serial.print("Eb         : ");  btct.printBytes(Eb,16);
+  Serial.print("Local Addr : ");  btct.printBytes(localAddress, 7);
+  Serial.print("LocalIOCap : ");  btct.printBytes(SlaveIOCap, 3);
+  Serial.print("MasterAddr : ");  btct.printBytes(remoteAddress, 7);
+  Serial.print("MasterIOCAP: ");  btct.printBytes(MasterIOCap, 3);
+#endif
+      
+  // Check if Ea = expectedEa
+  if (memcmp(Ea, expectedEa, 16) == 0){
+    // Check ok
+    // Send our confirmation value to complete authentication stage 2
+    uint8_t ret[17];
+    ret[0] = CONNECTION_PAIRING_DHKEY_CHECK;
+    for(int i=0; i<sizeof(Eb); i++){
+      ret[sizeof(Eb)-i] = Eb[i];
+    }
+    HCI.sendAclPkt(handle, SECURITY_CID, sizeof(ret), ret );
+    uint8_t encryption = ATT.getPeerEncryption(handle) | PEER_ENCRYPTION::SENT_DH_CHECK;
+    ATT.setPeerEncryption(handle, encryption);
+#ifdef _BLE_TRACE_
+    Serial.println("DHKey check ok - send Eb back");
+#endif
+
+  } else {
+    // Check failed, abort pairing
+    uint8_t ret[2] = {CONNECTION_PAIRING_FAILED, 0x0B}; // 0x0B = DHKey Check Failed
+    HCI.sendAclPkt(handle, SECURITY_CID, sizeof(ret), ret);
+    ATT.setPeerEncryption(handle, NO_ENCRYPTION);
+#ifdef _BLE_TRACE_
+    Serial.println("Error: DHKey check failed - Aborting");
+#endif
+  }
+}
 
 void L2CAPSignalingClass::removeConnection(uint8_t /*handle*/, uint16_t /*reason*/)
 {
@@ -124,6 +450,15 @@ void L2CAPSignalingClass::setSupervisionTimeout(uint16_t supervisionTimeout)
   _supervisionTimeout = supervisionTimeout;
 }
 
+void L2CAPSignalingClass::setPairingEnabled(uint8_t enabled)
+{
+  _pairing_enabled = enabled;
+}
+bool L2CAPSignalingClass::isPairingEnabled()
+{
+  return _pairing_enabled > 0;
+}
+
 void L2CAPSignalingClass::connectionParameterUpdateRequest(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[])
 {
   struct __attribute__ ((packed)) L2CAPConnectionParameterUpdateRequest {
diff --git a/src/utility/L2CAPSignaling.h b/src/utility/L2CAPSignaling.h
index 233eca7f..26042167 100644
--- a/src/utility/L2CAPSignaling.h
+++ b/src/utility/L2CAPSignaling.h
@@ -23,6 +23,33 @@
 #include <Arduino.h>
 
 #define SIGNALING_CID 0x0005
+#define SECURITY_CID 0x0006
+
+
+#define CONNECTION_PAIRING_REQUEST        0x01
+#define CONNECTION_PAIRING_RESPONSE       0x02
+#define CONNECTION_PAIRING_CONFIRM        0x03
+#define CONNECTION_PAIRING_RANDOM         0x04
+#define CONNECTION_PAIRING_FAILED         0x05
+#define CONNECTION_ENCRYPTION_INFORMATION 0x06
+#define CONNECTION_MASTER_IDENTIFICATION  0x07
+#define CONNECTION_IDENTITY_INFORMATION   0x08
+#define CONNECTION_IDENTITY_ADDRESS       0x09
+#define CONNECTION_SIGNING_INFORMATION    0x0A
+#define CONNECTION_SECURITY_REQUEST       0x0B
+#define CONNECTION_PAIRING_PUBLIC_KEY     0x0C
+#define CONNECTION_PAIRING_DHKEY_CHECK    0x0D
+#define CONNECTION_PAIRING_KEYPRESS       0x0E
+
+#define IOCAP_DISPLAY_ONLY         0x00
+#define IOCAP_DISPLAY_YES_NO       0x01
+#define IOCAP_KEYBOARD_ONLY        0x02
+#define IOCAP_NO_INPUT_NO_OUTPUT   0x03
+#define IOCAP_KEYBOARD_DISPLAY     0x04
+
+
+#define LOCAL_AUTHREQ 0b00101101
+// #define LOCAL_IOCAP   IOCAP_DISPLAY_ONLY // will use JustWorks pairing
 
 class L2CAPSignalingClass {
 public:
@@ -36,20 +63,32 @@ class L2CAPSignalingClass {
 
   virtual void handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]);
 
+  virtual void handleSecurityData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]);
+
   virtual void removeConnection(uint8_t handle, uint16_t reason);
 
   virtual void setConnectionInterval(uint16_t minInterval, uint16_t maxInterval);
 
   virtual void setSupervisionTimeout(uint16_t supervisionTimeout);
+  
+  virtual void setPairingEnabled(uint8_t enabled);
+  virtual bool isPairingEnabled();
+
+
+
+  virtual void smCalculateLTKandConfirm(uint16_t handle, uint8_t expectedEa[]);
+
 
 private:
   virtual void connectionParameterUpdateRequest(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]);
   virtual void connectionParameterUpdateResponse(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]);
 
+
 private:
   uint16_t _minInterval;
   uint16_t _maxInterval;
   uint16_t _supervisionTimeout;
+  uint8_t _pairing_enabled;
 };
 
 extern L2CAPSignalingClass& L2CAPSignaling;
diff --git a/src/utility/bitDescriptions.cpp b/src/utility/bitDescriptions.cpp
new file mode 100644
index 00000000..bf896bc1
--- /dev/null
+++ b/src/utility/bitDescriptions.cpp
@@ -0,0 +1,30 @@
+#include "bitDescriptions.h"
+
+
+#define BONDING_BIT  0b00000001
+#define MITM_BIT     0b00000100
+#define SC_BIT       0b00001000
+#define KEYPRESS_BIT 0b00010000
+#define CT2_BIT      0b00100000
+
+
+AuthReq::AuthReq(){}
+AuthReq::AuthReq(uint8_t octet):_octet(octet){}
+bool AuthReq::Bonding(){ return (_octet & BONDING_BIT)>0;}
+bool AuthReq::MITM(){ return (_octet & MITM_BIT)>0;}
+bool AuthReq::SC(){ return (_octet & SC_BIT)>0;}
+bool AuthReq::KeyPress(){ return (_octet & KEYPRESS_BIT)>0;}
+bool AuthReq::CT2(){ return (_octet & CT2_BIT)>0;}
+
+
+void AuthReq::setBonding(bool state) { _octet= state? _octet|BONDING_BIT  : _octet&~BONDING_BIT;}
+void AuthReq::setMITM(bool state)  { _octet= state? _octet|MITM_BIT   : _octet&~MITM_BIT;}
+void AuthReq::setSC(bool state){ _octet= state? _octet|SC_BIT : _octet&~SC_BIT;}
+void AuthReq::setKeyPress(bool state){ _octet= state? _octet|KEYPRESS_BIT : _octet&~KEYPRESS_BIT;}
+void AuthReq::setCT2(bool state){ _octet= state? _octet|CT2_BIT : _octet&~CT2_BIT;}
+
+uint8_t _octet;
+
+
+void AuthReq::setOctet( uint8_t octet){_octet = octet;}
+uint8_t AuthReq::getOctet() {return _octet;}
diff --git a/src/utility/bitDescriptions.h b/src/utility/bitDescriptions.h
new file mode 100644
index 00000000..6d32c52a
--- /dev/null
+++ b/src/utility/bitDescriptions.h
@@ -0,0 +1,41 @@
+#ifndef _BIT_DESCRIPTIONS_H_
+#define _BIT_DESCRIPTIONS_H_
+#include <Arduino.h>
+
+class AuthReq{
+public:
+    AuthReq();
+    AuthReq(uint8_t octet);
+    void setOctet( uint8_t octet);
+    uint8_t getOctet();
+
+
+    // The Bonding_Flags field is a 2-bit field that indicates the type of bonding being requested by the initiating device
+    bool Bonding();
+    // The MITM field is a 1-bit flag that is set to one if the device is requesting MITM protection
+    bool MITM();
+    // The SC field is a 1 bit flag. If LE Secure Connections pairing is supported by the device, then the SC field shall be set to 1, otherwise it shall be set to 0.
+    bool SC();
+    // The keypress field is a 1-bit flag that is used only in the Passkey Entry protocol and shall be ignored in other protocols.
+    bool KeyPress();
+    // The CT2 field is a 1-bit flag that shall be set to 1 upon transmission to indicate support for the h7 function.
+    bool CT2();
+
+    void setBonding(bool state);
+    void setMITM(bool state);
+    void setSC(bool state);
+    void setKeyPress(bool state);
+    void setCT2(bool state);
+private:
+    uint8_t _octet;
+};
+
+enum IOCap {
+    DisplayOnly,
+    DisplayYesNo,
+    KeyboardOnly,
+    NoInputNoOutput,
+    KeyboardDisplay
+};
+
+#endif
\ No newline at end of file
diff --git a/src/utility/btct.cpp b/src/utility/btct.cpp
new file mode 100644
index 00000000..199c9e0d
--- /dev/null
+++ b/src/utility/btct.cpp
@@ -0,0 +1,378 @@
+#include "btct.h"
+#include <Arduino.h>
+#include "HCI.h"
+#include "STM32duinoBLE.h"
+BluetoothCryptoToolbox::BluetoothCryptoToolbox(){}
+//    In step 1, AES-128 with key K is applied to an all-zero input block.
+//    In step 2, K1 is derived through the following operation:
+//    If the most significant bit of L is equal to 0, K1 is the left-shift
+//    of L by 1 bit.
+//    Otherwise, K1 is the exclusive-OR of const_Rb and the left-shift of L
+//    by 1 bit.
+//    In step 3, K2 is derived through the following operation:
+//    If the most significant bit of K1 is equal to 0, K2 is the left-shift
+//    of K1 by 1 bit.
+//    Otherwise, K2 is the exclusive-OR of const_Rb and the left-shift of
+//    K1 by 1 bit.
+//    In step 4, (K1,K2) := Generate_Subkey(K) is returned.
+unsigned char const_Rb[16] = {
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87
+  };
+
+#define DHKEY_LENGTH 32
+#define N_LEN 16
+#define ADDR_LEN 6
+#define LEN_LTK 16
+#define LEN_MAC_KEY 16
+
+void BluetoothCryptoToolbox::printBytes(uint8_t bytes[], uint8_t length){
+    for(int i=0; i<length; i++){
+        if(i>0){
+            Serial.print(", 0x");
+        }else{
+            Serial.print("0x");
+        }
+        Serial.print(bytes[i],HEX);
+    }
+    Serial.print('\n');
+}
+int BluetoothCryptoToolbox::f5(uint8_t DHKey[],uint8_t N_master[], uint8_t N_slave[],
+            uint8_t BD_ADDR_master[], uint8_t BD_ADDR_slave[], uint8_t MacKey[], uint8_t LTK[])
+{
+    uint8_t SALT[16] = {0x6C, 0x88, 0x83, 0x91, 0xAA, 0xF5, 0xA5, 0x38, 0x60, 0x37, 0x0B, 0xDB, 0x5A, 0x60, 0x83, 0xBE};
+    uint8_t keyID[4] = {0x62, 0x74, 0x6c, 0x65};
+    uint8_t length[2];
+    length[0] = 0x01;
+    length[1] = 0x00;
+#ifdef _BLE_TRACE_
+    Serial.print("Starting f5 calculation");
+    Serial.print("Using DHKey:  ");
+    printBytes(DHKey, DHKEY_LENGTH);
+    Serial.print("Using N_Master: ");
+    printBytes(N_master, N_LEN);
+    Serial.print("Using N_Slave:  ");
+    printBytes(N_slave, N_LEN);
+
+    Serial.println("Using BD_ADDR_MASTER: ");
+    printBytes(BD_ADDR_master, ADDR_LEN);
+    Serial.println("Using BD_ADDR_SLAVE:  ");
+    printBytes(BD_ADDR_slave, ADDR_LEN);
+#endif
+
+    uint8_t T[16];
+
+    struct __attribute__ ((packed)) CmacInput
+    {
+        uint8_t counter;
+        uint8_t keyID[4];
+        uint8_t N1[16];
+        uint8_t N2[16];
+        uint8_t A1[7];
+        uint8_t A2[7];
+        uint8_t length[2];
+    } cmacInput = {0,0,0,0,0,0,0};
+    cmacInput.counter = 0;
+    memcpy(cmacInput.keyID, keyID, 4);
+    memcpy(cmacInput.N1,N_master,16);
+    memcpy(cmacInput.N2,N_slave,16);
+    memcpy(cmacInput.A1,BD_ADDR_master,7);
+    memcpy(cmacInput.A2,BD_ADDR_slave,7);
+    memcpy(cmacInput.length,length,2);
+    AES_CMAC(SALT, DHKey, 32, T);
+
+    AES_CMAC(T, (uint8_t*)&cmacInput,sizeof(cmacInput), MacKey);
+    cmacInput.counter=1;
+    AES_CMAC(T, (uint8_t*)&cmacInput, sizeof(cmacInput), LTK);
+
+    return 1;
+}
+int BluetoothCryptoToolbox::f6(uint8_t W[], uint8_t N1[],uint8_t N2[],uint8_t R[], uint8_t IOCap[], uint8_t A1[], uint8_t A2[], uint8_t Ex[])
+{
+    struct __attribute__ ((packed)) F6Input
+    {
+        uint8_t N1[16];
+        uint8_t N2[16];
+        uint8_t R[16];
+        uint8_t IOCap[3];
+        uint8_t A1[7];
+        uint8_t A2[7];
+    } f6Input = {0,0,0,0,0,0};
+
+    memcpy(f6Input.N1, N1, 16);
+    memcpy(f6Input.N2, N2, 16);
+    memcpy(f6Input.R, R, 16);
+    memcpy(f6Input.IOCap, IOCap, 3);
+    memcpy(f6Input.A1, A1, 7);
+    memcpy(f6Input.A2, A2, 7);
+
+
+    AES_CMAC(W, (uint8_t*)&f6Input, sizeof(f6Input),Ex);
+    return 1;
+}
+// AES_CMAC from RFC
+int BluetoothCryptoToolbox::ah(uint8_t k[16], uint8_t r[3], uint8_t* result)
+{
+    uint8_t r_[16];
+    int i=0;
+    for(i=0; i<16; i++) r_[i] = 0;
+    for(i=0; i<3; i++)  r_[i+13] = r[i];
+    uint8_t intermediate[16];
+    AES_128(k,r_,intermediate);
+    for(i=0; i<3; i++){
+        result[i] = intermediate[i+13];
+    }
+    return 1;
+}
+void BluetoothCryptoToolbox::testAh()
+{
+    uint8_t irk[16] = {0xec,0x02,0x34,0xa3,0x57,0xc8,0xad,0x05,0x34,0x10,0x10,0xa6,0x0a,0x39,0x7d,0x9b};         
+    uint8_t expected_final[3] = {0x0d,0xfb,0xaa};
+    
+    uint8_t ourResult[3];
+    ah(irk, expected_final, ourResult);
+
+
+    Serial.print("Expected   : ");
+    printBytes(&expected_final[3], 3);
+    Serial.print("Actual     : ");
+    printBytes(ourResult, 3);
+}
+
+int BluetoothCryptoToolbox::g2(uint8_t U[], uint8_t V[], uint8_t X[], uint8_t Y[], uint8_t out[4])
+{
+    struct __attribute__ ((packed)) CmacInput {
+        uint8_t U[32];
+        uint8_t V[32];
+        uint8_t Y[16];
+    } cmacInput= {0,0,0};
+    memcpy(cmacInput.U,U,32);
+    memcpy(cmacInput.V,V,32);
+    memcpy(cmacInput.Y,Y,16);
+    uint8_t intermediate[16];
+    AES_CMAC(X,(uint8_t*)&cmacInput,sizeof(CmacInput),intermediate);
+    memcpy(out,&intermediate[12],4);
+    return 1;
+}
+void BluetoothCryptoToolbox::testg2(){
+    uint8_t U[32] = {0x20,0xb0,0x03,0xd2,0xf2,0x97,0xbe,0x2c,0x5e,0x2c,0x83,0xa7,0xe9,0xf9,0xa5,0xb9,0xef,0xf4,0x91,0x11,0xac,0xf4,0xfd,0xdb,0xcc,0x03,0x01,0x48,0x0e,0x35,0x9d,0xe6};
+    uint8_t V[32] = {0x55,0x18,0x8b,0x3d,0x32,0xf6,0xbb,0x9a,0x90,0x0a,0xfc,0xfb,0xee,0xd4,0xe7,0x2a,0x59,0xcb,0x9a,0xc2,0xf1,0x9d,0x7c,0xfb,0x6b,0x4f,0xdd,0x49,0xf4,0x7f,0xc5,0xfd};
+    uint8_t X[16] = {0xd5,0xcb,0x84,0x54,0xd1,0x77,0x73,0x3e,0xff,0xff,0xb2,0xec,0x71,0x2b,0xae,0xab};
+    uint8_t Y[16] = {0xa6,0xe8,0xe7,0xcc,0x25,0xa7,0x5f,0x6e,0x21,0x65,0x83,0xf7,0xff,0x3d,0xc4,0xcf};
+    uint8_t out[4];
+    
+    
+    uint32_t expected = 0;
+    g2(U,V,X,Y,out);
+    uint32_t result = 0;
+    for(int i=0; i<4; i++) result += out[i] << 8*i;
+
+    Serial.print("Expected :     ");
+    Serial.println(expected);
+    Serial.print("Result   : ");
+    Serial.println(result);
+    Serial.println();
+
+}
+
+void BluetoothCryptoToolbox::AES_CMAC ( unsigned char *key, unsigned char *input, int length,
+                  unsigned char *mac )
+{
+    unsigned char       X[16],Y[16], M_last[16], padded[16];
+    unsigned char       K1[16], K2[16];
+    int         n, i, flag;
+    generateSubkey(key,K1,K2);
+
+    n = (length+15) / 16;       /* n is number of rounds */
+
+    if ( n == 0 ) {
+        n = 1;
+        flag = 0;
+    } else {
+        if ( (length%16) == 0 ) { /* last block is a complete block */
+            flag = 1;
+        } else { /* last block is not complete block */
+            flag = 0;
+        }
+    }
+
+    if ( flag ) { /* last block is complete block */
+        xor_128(&input[16*(n-1)],K1,M_last);
+    } else {
+        padding(&input[16*(n-1)],padded,length%16);
+        xor_128(padded,K2,M_last);
+    }
+
+    for ( i=0; i<16; i++ ) X[i] = 0;
+    for ( i=0; i<n-1; i++ ) {
+        xor_128(X,&input[16*i],Y); /* Y := Mi (+) X  */
+        AES_128(key,Y,X);      /* X := AES-128(KEY, Y); */
+    }
+
+    xor_128(X,M_last,Y);
+    AES_128(key,Y,X);
+
+    for ( i=0; i<16; i++ ) {
+        mac[i] = X[i];
+    }
+}
+// Paddinng function from RFC
+void BluetoothCryptoToolbox::padding( unsigned char *lastb, unsigned char *pad, int length )
+{
+    int j;
+    /* original last block */
+    for ( j=0; j<16; j++ ) {
+        if ( j < length ) {
+            pad[j] = lastb[j];
+        } else if ( j == length ) {
+            pad[j] = 0x80;
+        } else {
+            pad[j] = 0x00;
+        }
+    }
+}
+// Generate subkey from RFC
+void BluetoothCryptoToolbox::generateSubkey(uint8_t* key, uint8_t* K1, uint8_t* K2){
+    unsigned char L[16];
+    unsigned char Z[16];
+    unsigned char tmp[16];
+    int i;
+
+    for ( i=0; i<16; i++ ) Z[i] = 0;
+
+    AES_128(key,Z,L);
+
+    if ( (L[0] & 0x80) == 0 ) { /* If MSB(L) = 0, then K1 = L << 1 */
+        leftshift_onebit(L,K1);
+    } else {    /* Else K1 = ( L << 1 ) (+) Rb */
+
+        leftshift_onebit(L,tmp);
+        xor_128(tmp,const_Rb,K1);
+    }
+
+    if ( (K1[0] & 0x80) == 0 ) {
+        leftshift_onebit(K1,K2);
+    } else {
+        leftshift_onebit(K1,tmp);
+        xor_128(tmp,const_Rb,K2);
+    }
+    return;
+}
+// Use BLE AES function - restart bluetooth if crash
+int BluetoothCryptoToolbox::AES_128(uint8_t* key, uint8_t* data_in, uint8_t* data_out){
+    uint8_t status = 0;
+    int n = 0;
+    int tries = 30;
+    while(HCI.leEncrypt(key,data_in, &status, data_out)!=1&&n<tries){
+        Serial.print("AES failed... retrying: ");
+        Serial.println(n);
+        BLE.end();
+        delay(200);
+        BLE.begin();
+        n++;
+        delay(100*n);
+    }
+    if(n==tries){
+        Serial.println("something went wrong with AES.");
+        return 0;
+    }
+    return 1;
+}
+// Tests AES CMAC
+#ifdef _BLE_TRACE_
+void BluetoothCryptoToolbox::test(){
+    unsigned char L[16];
+    unsigned char Z[16];
+    unsigned char tmp[16];
+    int i;
+
+    for ( i=0; i<16; i++ ) Z[i] = 0x00;
+    uint8_t k[16]            = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
+    
+    Serial.println("AES Plaintext:");
+    for(int i=0; i<16; i++){
+        Serial.print(" 0x");
+        Serial.print(Z[i],HEX);
+    }
+    Serial.println(".");
+    uint8_t expected_aes[16] = {0x7d, 0xf7, 0x6b, 0x0c, 0x1a, 0xb8, 0x99, 0xb3, 0x3e, 0x42, 0xf0, 0x47, 0xb9, 0x1b, 0x54, 0x6f};
+
+    AES_128(k, Z, L);
+    for(int i=0; i<16; i++){
+        Serial.print(" 0x");
+        Serial.print(L[i],HEX);
+    }
+    Serial.println(".");
+    for(int i=0; i<16; i++){
+        Serial.print(" 0x");
+        Serial.print(expected_aes[i],HEX);
+    }
+    Serial.println(".");
+
+    uint8_t k1[16];
+    uint8_t k2[16];
+    generateSubkey(k,k1,k2);
+    uint8_t expected_k1[16]  = {0xfb, 0xee, 0xd6, 0x18, 0x35, 0x71, 0x33, 0x66, 0x7c, 0x85, 0xe0, 0x8f, 0x72, 0x36, 0xa8, 0xde};
+    uint8_t expected_k2[16]  = {0xf7, 0xdd, 0xac, 0x30, 0x6a, 0xe2, 0x66, 0xcc, 0xf9, 0x0b, 0xc1, 0x1e, 0xe4, 0x6d, 0x51, 0x3b};
+
+    for(int i=0; i<16; i++){
+        Serial.print(" 0x");
+        Serial.print(k2[i],HEX);
+    }
+    Serial.println(".");
+    for(int i=0; i<16; i++){
+        Serial.print(" 0x");
+        Serial.print(expected_k2[i],HEX);
+    }
+    Serial.println(".");
+    for(int i=0; i<16; i++){
+        Serial.print(" 0x");
+        Serial.print(k1[i],HEX);
+    }
+    Serial.println(".");
+    for(int i=0; i<16; i++){
+        Serial.print(" 0x");
+        Serial.print(expected_k1[i],HEX);
+    }
+    Serial.println(".");
+
+    uint8_t m[40] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11};
+    uint8_t mac[16];
+    uint8_t expected_mac[16] = {0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27};
+    AES_CMAC(k,m,40,mac);
+
+    for(int i=0; i<16; i++){
+        Serial.print(" 0x");
+        Serial.print(mac[i],HEX);
+    }
+    Serial.println(".");
+    for(int i=0; i<16; i++){
+        Serial.print(" 0x");
+        Serial.print(expected_mac[i],HEX);
+    }
+    Serial.println(".");
+}
+#endif
+// From RFC
+void BluetoothCryptoToolbox::leftshift_onebit(unsigned char *input,unsigned char *output)
+{
+    int i;
+    unsigned char overflow = 0;
+
+    for ( i=15; i>=0; i-- ) {
+        output[i] = input[i] << 1;
+        output[i] |= overflow;
+        overflow = (input[i] & 0x80)?1:0;
+    }
+    return;
+}
+// From RFC
+void BluetoothCryptoToolbox::xor_128(unsigned char *a, unsigned char *b, unsigned char *out)
+{
+    int i;
+    for (i=0;i<16; i++)
+    {
+        out[i] = a[i] ^ b[i];
+    }
+}
+BluetoothCryptoToolbox btct;
\ No newline at end of file
diff --git a/src/utility/btct.h b/src/utility/btct.h
new file mode 100644
index 00000000..08f8f192
--- /dev/null
+++ b/src/utility/btct.h
@@ -0,0 +1,30 @@
+#ifndef _BTCT_H_
+#define _BTCT_H_
+#include <Arduino.h>
+
+// Implementation of functions defined in BTLE standard
+class BluetoothCryptoToolbox{
+public:
+    BluetoothCryptoToolbox();
+    void printBytes(uint8_t bytes[], uint8_t length);
+    void generateSubkey(uint8_t* K, uint8_t* K1, uint8_t* K2);
+    void AES_CMAC ( unsigned char *key, unsigned char *input, int length,
+                  unsigned char *mac );
+    int f5(uint8_t DHKey[],uint8_t N_master[], uint8_t N_slave[],
+            uint8_t BD_ADDR_master[], uint8_t BD_ADDR_slave[], uint8_t MacKey[], uint8_t LTK[]);
+    int f6(uint8_t W[], uint8_t N1[],uint8_t N2[],uint8_t R[], uint8_t IOCap[], uint8_t A1[], uint8_t A2[], uint8_t Ex[]);
+    int g2(uint8_t U[], uint8_t V[], uint8_t X[], uint8_t Y[], uint8_t out[4]);
+    int ah(uint8_t k[16], uint8_t r[3], uint8_t result[3]);
+    void test();
+    void testF5();
+    void testF6();
+    void testAh();
+    void testg2();
+private:
+    int AES_128(uint8_t key[], uint8_t data_in[], uint8_t data_out[]);
+    void leftshift_onebit(unsigned char *input,unsigned char *output);
+    void xor_128(unsigned char *a, unsigned char *b, unsigned char *out);
+    void padding ( unsigned char *lastb, unsigned char *pad, int length );
+};
+extern BluetoothCryptoToolbox btct;
+#endif
\ No newline at end of file
diff --git a/src/utility/keyDistribution.cpp b/src/utility/keyDistribution.cpp
new file mode 100644
index 00000000..f754366c
--- /dev/null
+++ b/src/utility/keyDistribution.cpp
@@ -0,0 +1,24 @@
+#include "keyDistribution.h"
+
+KeyDistribution::KeyDistribution():_octet(0){}
+KeyDistribution::KeyDistribution(uint8_t octet):_octet(octet){}
+
+#define ENCKEY  0b00000001
+#define IDKEY   0b00000010
+#define SIGNKEY 0b00000100
+#define LINKKEY 0b00001000
+void KeyDistribution::setOctet( uint8_t octet){_octet = octet;}
+uint8_t KeyDistribution::getOctet() {return _octet;}
+// Ignored when SMP is on LE transport
+bool KeyDistribution::EncKey(){ return (_octet & ENCKEY)>0;}
+// Device shall distribute IRK using Identity information command followed by its address using Identity address information
+bool KeyDistribution::IdKey(){ return  (_octet & IDKEY)>0;}
+// Device shall distribute CSRK using signing information command
+bool KeyDistribution::SignKey(){ return (_octet & SIGNKEY)>0;}
+// Device would like to derive BR/EDR from LTK
+bool KeyDistribution::LinkKey(){ return (_octet & LINKKEY)>0;}
+
+void KeyDistribution::setEncKey(bool state) { _octet= state? _octet|ENCKEY  : _octet&~ENCKEY;}
+void KeyDistribution::setIdKey(bool state)  { _octet= state? _octet|IDKEY   : _octet&~IDKEY;}
+void KeyDistribution::setSignKey(bool state){ _octet= state? _octet|SIGNKEY : _octet&~SIGNKEY;}
+void KeyDistribution::setLinkKey(bool state){ _octet= state? _octet|LINKKEY : _octet&~LINKKEY;}
\ No newline at end of file
diff --git a/src/utility/keyDistribution.h b/src/utility/keyDistribution.h
new file mode 100644
index 00000000..d78fcc1a
--- /dev/null
+++ b/src/utility/keyDistribution.h
@@ -0,0 +1,29 @@
+#ifndef _KEY_DISTRIBUTION_H_
+#define _KEY_DISTRIBUTION_H_
+#include <Arduino.h>
+
+class KeyDistribution{
+public:
+    KeyDistribution();
+    KeyDistribution(uint8_t octet);
+    void setOctet( uint8_t octet);
+    uint8_t getOctet();
+    // Ignored when SMP is on LE transport
+    bool EncKey();
+    // Device shall distribute IRK using Identity information command followed by its address using Identity address information
+    bool IdKey();
+    // Device shall distribute CSRK using signing information command
+    bool SignKey();
+    // Device would like to derive BR/EDR from LTK
+    bool LinkKey();
+
+    void setEncKey(bool state);
+    void setIdKey(bool state);
+    void setSignKey(bool state);
+    void setLinkKey(bool state);
+private:
+    uint8_t _octet;
+    // 1.   IRK by the slave2.   BD ADDR by the slave3.   CSRK by the slave4.   IRK by the master5.   BD_ADDR by the master6.   CSRK by the master
+};
+
+#endif
\ No newline at end of file