]> git.tnoah.ca Git - lora-radio.git/commitdiff
nRF52 BLE improvements
authorjacob.eva <x00010@disroot.org>
Thu, 10 Oct 2024 13:40:57 +0000 (14:40 +0100)
committerjacob.eva <x00010@disroot.org>
Thu, 10 Oct 2024 13:40:57 +0000 (14:40 +0100)
Bluetooth.h
Config.h
Utilities.h

index 7d23c79f593256990f93fb65f81ca78b1359792c..9802d56b1937200f77336fbec51021489f97203b 100644 (file)
@@ -387,129 +387,169 @@ char bt_devname[11];
     bt_ssp_pin = 0;
     bt_state = BT_STATE_ON;
   }
-
-  void bt_pairing_complete(uint16_t conn_handle, uint8_t auth_status) {
-      if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS) {
-        bt_disable_pairing();
+void bt_pairing_complete(uint16_t conn_handle, uint8_t auth_status) {
+    if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS) {
+      BLEConnection* connection = Bluefruit.Connection(conn_handle);
+
+      ble_gap_conn_sec_mode_t security = connection->getSecureMode();
+
+      // On the NRF52 it is not possible with the Arduino library to reject
+      // requests from devices with no IO capabilities, which would allow
+      // bypassing pin entry through pairing using the "just works" mode.
+      // Therefore, we must check the security level of the connection after
+      // pairing to ensure "just works" has not been used. If it has, we need
+      // to disconnect, unpair and delete any bonding information immediately.
+      // Settings on the SerialBT service should prevent unauthorised access to
+      // the serial port anyway, but this is still wise to do regardless.
+      //
+      // Note: It may be nice to have this done in the BLESecurity class in the
+      // future, but as it stands right now I'd have to fork the BSP to do
+      // that, which I don't fancy doing. Impact on security is likely minimal.
+      // Requires investigation.
+
+      if (security.sm == 1 && security.lv >= 3) {
+          bt_state = BT_STATE_CONNECTED;
+          cable_state = CABLE_STATE_DISCONNECTED;
+          bt_disable_pairing();
       } else {
-        bt_ssp_pin = 0;
-      }
-  }
-
-  bool bt_passkey_callback(uint16_t conn_handle, uint8_t const passkey[6], bool match_request) {
-      for (int i = 0; i < 6; i++) {
-          // multiply by tens however many times needed to make numbers appear in order
-          bt_ssp_pin += ((int)passkey[i] - 48) * pow(10, 5-i);
-      }
-      kiss_indicate_btpin();
-      if (match_request) {
-          if (bt_allow_pairing) {
-              return true;
+          if (connection->bonded()) {
+              connection->removeBondKey();
           }
+          connection->disconnect();
       }
-      return false;
-  }
+    } else {
+      bt_ssp_pin = 0;
+    }
+}
 
-  void bt_connect_callback(uint16_t conn_handle) {
-      bt_state = BT_STATE_CONNECTED;
-      cable_state = CABLE_STATE_DISCONNECTED;
-  }
+bool bt_passkey_callback(uint16_t conn_handle, uint8_t const passkey[6], bool match_request) {
+    for (int i = 0; i < 6; i++) {
+        // multiply by tens however many times needed to make numbers appear in order
+        bt_ssp_pin += ((int)passkey[i] - 48) * pow(10, 5-i);
+    }
+    kiss_indicate_btpin();
+    if (bt_allow_pairing) {
+        return true;
+    }
+    return false;
+}
 
-  void bt_disconnect_callback(uint16_t conn_handle, uint8_t reason) {
-      bt_state = BT_STATE_ON;
-  }
+void bt_connect_callback(uint16_t conn_handle) {
+    bt_state = BT_STATE_CONNECTED;
+    cable_state = CABLE_STATE_DISCONNECTED;
+
+    //BLEConnection* conn = Bluefruit.Connection(conn_handle);
+    conn->requestPHY(BLE_GAP_PHY_2MBPS);
+    conn->requestMtuExchange(512+3);
+    conn->requestDataLengthUpdate();
+}
+
+void bt_disconnect_callback(uint16_t conn_handle, uint8_t reason) {
+    if (reason != BLE_GAP_SEC_STATUS_SUCCESS) {
+        bt_state = BT_STATE_ON;
+    }
+}
 
-  bool bt_setup_hw() {
-    if (!bt_ready) {
+bool bt_setup_hw() {
+  if (!bt_ready) {
+    #if HAS_EEPROM 
+        if (EEPROM.read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) {
+    #else
+        if (eeprom_read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) {
+    #endif
+      bt_enabled = true;
+    } else {
+      bt_enabled = false;
+    }
+    Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
+    Bluefruit.autoConnLed(false);
+    if (Bluefruit.begin()) {
+      Bluefruit.setTxPower(8);    // Check bluefruit.h for supported values
+      Bluefruit.Security.setIOCaps(true, false, false); // display, yes; yes / no, no; keyboard, no
+      // This device is indeed capable of yes / no through the pairing mode
+      // being set, but I have chosen to set it thus to force the input of the
+      // pin on the device initiating the pairing.
+
+      Bluefruit.Security.setMITM(true);
+      Bluefruit.Security.setPairPasskeyCallback(bt_passkey_callback);
+      Bluefruit.Security.setSecuredCallback(bt_connect_callback);
+      Bluefruit.Periph.setDisconnectCallback(bt_disconnect_callback);
+      Bluefruit.Security.setPairCompleteCallback(bt_pairing_complete);
+      //Bluefruit.Periph.setConnInterval(6, 12); // 7.5 - 15 ms
+
+      const ble_gap_addr_t gap_addr = Bluefruit.getAddr();
+      char *data = (char*)malloc(BT_DEV_ADDR_LEN+1);
+      for (int i = 0; i < BT_DEV_ADDR_LEN; i++) {
+          data[i] = gap_addr.addr[i];
+      }
       #if HAS_EEPROM 
-          if (EEPROM.read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) {
+          data[BT_DEV_ADDR_LEN] = EEPROM.read(eeprom_addr(ADDR_SIGNATURE));
       #else
-          if (eeprom_read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) {
+          data[BT_DEV_ADDR_LEN] = eeprom_read(eeprom_addr(ADDR_SIGNATURE));
       #endif
-        bt_enabled = true;
-      } else {
-        bt_enabled = false;
-      }
-      Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
-      Bluefruit.autoConnLed(false);
-      if (Bluefruit.begin()) {
-        Bluefruit.setTxPower(4);    // Check bluefruit.h for supported values
-        Bluefruit.Security.setIOCaps(true, true, false);
-        Bluefruit.Security.setPairPasskeyCallback(bt_passkey_callback);
-        Bluefruit.Periph.setConnectCallback(bt_connect_callback);
-        Bluefruit.Periph.setDisconnectCallback(bt_disconnect_callback);
-        Bluefruit.Security.setPairCompleteCallback(bt_pairing_complete);
-        const ble_gap_addr_t gap_addr = Bluefruit.getAddr();
-        char *data = (char*)malloc(BT_DEV_ADDR_LEN+1);
-        for (int i = 0; i < BT_DEV_ADDR_LEN; i++) {
-            data[i] = gap_addr.addr[i];
-        }
-        #if HAS_EEPROM 
-            data[BT_DEV_ADDR_LEN] = EEPROM.read(eeprom_addr(ADDR_SIGNATURE));
-        #else
-            data[BT_DEV_ADDR_LEN] = eeprom_read(eeprom_addr(ADDR_SIGNATURE));
-        #endif
-        unsigned char *hash = MD5::make_hash(data, BT_DEV_ADDR_LEN);
-        memcpy(bt_dh, hash, BT_DEV_HASH_LEN);
-        sprintf(bt_devname, "RNode %02X%02X", bt_dh[14], bt_dh[15]);
-        free(data);
-
-        bt_ready = true;
-        return true;
+      unsigned char *hash = MD5::make_hash(data, BT_DEV_ADDR_LEN);
+      memcpy(bt_dh, hash, BT_DEV_HASH_LEN);
+      sprintf(bt_devname, "RNode %02X%02X", bt_dh[14], bt_dh[15]);
+      free(data);
 
-      } else { return false; }
-    } else { return false; }
-  }
+      bt_ready = true;
+      return true;
 
-  void bt_start() {
-    if (bt_state == BT_STATE_OFF) {
-      Bluefruit.setName(bt_devname);
-      bledis.setManufacturer(BLE_MANUFACTURER);
-      bledis.setModel(BLE_MODEL);
-      // start device information service
-      bledis.begin();
+    } else { return false; }
+  } else { return false; }
+}
 
-      SerialBT.begin();
+void bt_start() {
+  if (bt_state == BT_STATE_OFF) {
+    Bluefruit.setName(bt_devname);
+    bledis.setManufacturer(BLE_MANUFACTURER);
+    bledis.setModel(BLE_MODEL);
+    // start device information service
+    bledis.begin();
 
-      blebas.begin();
+    SerialBT.bufferTXD(true); // enable buffering
 
-      // non-connectable advertising
-      Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
-      Bluefruit.Advertising.addTxPower();
+    SerialBT.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); // enable encryption for BLE serial
+    SerialBT.begin();
 
-      // Include bleuart 128-bit uuid
-      Bluefruit.Advertising.addService(SerialBT);
+    blebas.begin();
 
-      // There is no room for Name in Advertising packet
-      // Use Scan response for Name
-      Bluefruit.ScanResponse.addName();
+    Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
+    Bluefruit.Advertising.addTxPower();
 
-      Bluefruit.Advertising.start(0);
+    // Include bleuart 128-bit uuid
+    Bluefruit.Advertising.addService(SerialBT);
 
-      bt_state = BT_STATE_ON;
-     }
-  }
+    // There is no room for Name in Advertising packet
+    // Use Scan response for Name
+    Bluefruit.ScanResponse.addName();
 
-  bool bt_init() {
-      bt_state = BT_STATE_OFF;
-      if (bt_setup_hw()) {
-        if (bt_enabled && !console_active) bt_start();
-        return true;
-      } else {
-        return false;
-      }
-  }
+    Bluefruit.Advertising.start(0);
 
-  void bt_enable_pairing() {
-    if (bt_state == BT_STATE_OFF) bt_start();
-    bt_allow_pairing = true;
-    bt_pairing_started = millis();
-    bt_state = BT_STATE_PAIRING;
-  }
+    bt_state = BT_STATE_ON;
+   }
+}
 
-  void update_bt() {
-    if (bt_allow_pairing && millis()-bt_pairing_started >= BT_PAIRING_TIMEOUT) {
-      bt_disable_pairing();
+bool bt_init() {
+    bt_state = BT_STATE_OFF;
+    if (bt_setup_hw()) {
+      if (bt_enabled && !console_active) bt_start();
+      return true;
+    } else {
+      return false;
     }
+}
+
+void bt_enable_pairing() {
+  if (bt_state == BT_STATE_OFF) bt_start();
+  bt_allow_pairing = true;
+  bt_pairing_started = millis();
+  bt_state = BT_STATE_PAIRING;
+}
+
+void update_bt() {
+  if (bt_allow_pairing && millis()-bt_pairing_started >= BT_PAIRING_TIMEOUT) {
+    bt_disable_pairing();
   }
+}
 #endif
index d10cc607e6427c85a661de6f9e0f75a47f27e01a..bc3704ad44a420cec6aa638ef1cdb5bc39f4fdc2 100644 (file)
--- a/Config.h
+++ b/Config.h
     bool device_init_done = false;
     bool eeprom_ok = false;
     bool firmware_update_mode = false;
+    bool serial_in_frame = false;
 
        // Boot flags
        #define START_FROM_BOOTLOADER 0x01
index 0a3fffb92c7da648aa068e86df87277344b89e82..7a2983e6bf224c00a0064b26a804177aa0dcc929 100644 (file)
@@ -668,6 +668,19 @@ void serial_write(uint8_t byte) {
                        Serial.write(byte);
                } else {
                        SerialBT.write(byte);
+
+            #if MCU_VARIANT == MCU_NRF52 && HAS_BLE
+            // This ensures that the TX buffer is flushed after a frame is queued in serial.
+            // serial_in_frame is used to ensure that the flush only happens at the end of the frame
+            if (serial_in_frame && byte == FEND) {
+                SerialBT.flushTXD();
+                serial_in_frame = false;
+            }
+            else if (!serial_in_frame && byte == FEND) {
+                serial_in_frame = true;
+            }
+            #endif
+
                }
        #else
                Serial.write(byte);