]> git.tnoah.ca Git - lora-radio.git/commitdiff
Implemented ESP32 device init and firmware hash checks
authorMark Qvist <mark@unsigned.io>
Tue, 1 Nov 2022 20:11:41 +0000 (21:11 +0100)
committerMark Qvist <mark@unsigned.io>
Tue, 1 Nov 2022 20:11:41 +0000 (21:11 +0100)
Bluetooth.h
Config.h
Device.h [new file with mode: 0644]
Framing.h
RNode_Firmware.ino
Utilities.h

index 2e9f2a992bd09734faf06004bf453755c081ef8a..01c357669baeba196f2ab7e00c668de89eb84509 100644 (file)
@@ -8,6 +8,7 @@ uint32_t bt_pairing_started = 0;
 
 #define BT_DEV_ADDR_LEN 6
 #define BT_DEV_HASH_LEN 16
+uint8_t dev_bt_mac[BT_DEV_ADDR_LEN];
 char bt_da[BT_DEV_ADDR_LEN];
 char bt_dh[BT_DEV_HASH_LEN];
 char bt_devname[11];
@@ -70,33 +71,52 @@ char bt_devname[11];
     }
   }
 
-  bool bt_setup_hw() {
-    if (!bt_ready) {
-      if (EEPROM.read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) { bt_enabled = true; } else { bt_enabled = false; }
+  bool bt_early_init_done = false;
+  bool bt_early_init() {
+    if (!bt_early_init_done) {
       if (btStart()) {
         if (esp_bluedroid_init() == ESP_OK) {
           if (esp_bluedroid_enable() == ESP_OK) {
             const uint8_t* bda_ptr = esp_bt_dev_get_address();
-            char *data = (char*)malloc(BT_DEV_ADDR_LEN+1);
-            for (int i = 0; i < BT_DEV_ADDR_LEN; i++) {
-                data[i] = bda_ptr[i];
-            }
-            data[BT_DEV_ADDR_LEN] = EEPROM.read(eeprom_addr(ADDR_SIGNATURE));
-            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);
-
-            SerialBT.enableSSP();
-            SerialBT.onConfirmRequest(bt_confirm_pairing);
-            SerialBT.onAuthComplete(bt_pairing_complete);
-            SerialBT.register_callback(bt_connection_callback);
-            
-            bt_ready = true;
+            memcpy(dev_bt_mac, bda_ptr, BT_DEV_HASH_LEN);
             return true;
+          } else {
+            return false;
+          }
+        } else {
+          return false;
+        }
+      } else {
+        return false;
+      }
+    } else {
+      return true;
+    }
+  }
+
+  bool bt_setup_hw() {
+    if (!bt_ready) {
+      if (EEPROM.read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) { bt_enabled = true; } else { bt_enabled = false; }
+      if (bt_early_init()) {
+        const uint8_t* bda_ptr = esp_bt_dev_get_address();
+        char *data = (char*)malloc(BT_DEV_ADDR_LEN+1);
+        for (int i = 0; i < BT_DEV_ADDR_LEN; i++) {
+          data[i] = bda_ptr[i];
+        }
+        data[BT_DEV_ADDR_LEN] = EEPROM.read(eeprom_addr(ADDR_SIGNATURE));
+        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);
+
+        SerialBT.enableSSP();
+        SerialBT.onConfirmRequest(bt_confirm_pairing);
+        SerialBT.onAuthComplete(bt_pairing_complete);
+        SerialBT.register_callback(bt_connection_callback);
+        
+        bt_ready = true;
+        return true;
 
-          } else { return false; }
-        } else { return false; }
       } else { return false; }
     }
   }
index 5939d2cdcfc4bc71a5281f89c4aae310ae5c8a7c..a9e8b0211ab6a169b7f80536923ac4a328699d39 100644 (file)
--- a/Config.h
+++ b/Config.h
@@ -60,7 +60,7 @@
        #define HEADER_L   1
        #define MIN_L      1
 
-       #define CMD_L      10
+       #define CMD_L      64
 
        // MCU dependent configuration parameters
 
        uint8_t pbuf[MTU];
 
        // KISS command buffer
-       uint8_t cbuf[CMD_L];
+       uint8_t cmdbuf[CMD_L];
 
        // LoRa transmit buffer
        uint8_t tbuf[MTU];
     bool external_power = false;
     float battery_voltage = 0.0;
     float battery_percent = 0.0;
-    // float auxillary_temperature = 0.0;
     uint8_t battery_state = 0x00;
+    uint8_t display_intensity = 0xFF;
+    bool device_init_done = false;
 
        // Boot flags
        #define START_FROM_BOOTLOADER 0x01
diff --git a/Device.h b/Device.h
new file mode 100644 (file)
index 0000000..612e964
--- /dev/null
+++ b/Device.h
@@ -0,0 +1,136 @@
+#include <Ed25519.h>
+#include "mbedtls/md.h"
+#include "esp_ota_ops.h"
+#include "esp_flash_partitions.h"
+#include "esp_partition.h"
+
+// Forward declaration from Utilities.h
+void eeprom_update(int mapped_addr, uint8_t byte);
+void hard_reset(void);
+
+const uint8_t dev_keys [] PROGMEM = {
+   0x0f, 0x15, 0x86, 0x74, 0xa0, 0x7d, 0xf2, 0xde, 0x32, 0x11, 0x29, 0xc1, 0x0d, 0xda, 0xcc, 0xc3,
+   0xe1, 0x9b, 0xac, 0xf2, 0x27, 0x06, 0xee, 0x89, 0x1f, 0x7a, 0xfc, 0xc3, 0x6a, 0xf5, 0x38, 0x08
+};
+
+#define DEV_SIG_LEN 64
+uint8_t dev_sig[DEV_SIG_LEN];
+
+#define DEV_KEY_LEN 32
+uint8_t dev_k_prv[DEV_KEY_LEN];
+uint8_t dev_k_pub[DEV_KEY_LEN];
+
+#define DEV_HASH_LEN 32
+uint8_t dev_hash[DEV_HASH_LEN];
+uint8_t dev_partition_table_hash[DEV_HASH_LEN];
+uint8_t dev_bootloader_hash[DEV_HASH_LEN];
+uint8_t dev_firmware_hash[DEV_HASH_LEN];
+uint8_t dev_firmware_hash_target[DEV_HASH_LEN];
+
+#define EEPROM_SIG_LEN 128
+uint8_t dev_eeprom_signature[EEPROM_SIG_LEN];
+
+bool dev_signature_validated = false;
+bool fw_signature_validated = true;
+
+#define DEV_SIG_OFFSET EEPROM_SIZE-EEPROM_RESERVED-DEV_SIG_LEN
+#define dev_sig_addr(a) (a+DEV_SIG_OFFSET)
+
+#define DEV_FWHASH_OFFSET EEPROM_SIZE-EEPROM_RESERVED-DEV_SIG_LEN-DEV_HASH_LEN
+#define dev_fwhash_addr(a) (a+DEV_FWHASH_OFFSET)
+
+bool device_signatures_ok() {
+  return dev_signature_validated && fw_signature_validated;
+}
+
+void device_validate_signature() {
+  int n_keys = sizeof(dev_keys)/DEV_KEY_LEN;
+  bool valid_signature_found = false;
+  for (int i = 0; i < n_keys; i++) {
+    memcpy(dev_k_pub, dev_keys+DEV_KEY_LEN*i, DEV_KEY_LEN);
+    if (Ed25519::verify(dev_sig, dev_k_pub, dev_hash, DEV_HASH_LEN)) {
+        valid_signature_found = true;
+    }
+  }
+
+  if (valid_signature_found) {
+    dev_signature_validated = true;
+  } else {
+    dev_signature_validated = false;
+  }
+}
+
+void device_save_signature() {
+  device_validate_signature();
+  if (dev_signature_validated) {
+    for (uint8_t i = 0; i < DEV_SIG_LEN; i++) {
+      eeprom_update(dev_sig_addr(i), dev_sig[i]);
+    }
+  }
+}
+
+void device_load_signature() {
+  for (uint8_t i = 0; i < DEV_SIG_LEN; i++) {
+    dev_sig[i] = EEPROM.read(dev_sig_addr(i));
+  }
+}
+
+void device_load_firmware_hash() {
+  Serial.println("Loading hash from EEPROM");
+  for (uint8_t i = 0; i < DEV_HASH_LEN; i++) {
+    dev_firmware_hash_target[i] = EEPROM.read(dev_fwhash_addr(i));
+  }
+}
+
+void device_save_firmware_hash() {
+  for (uint8_t i = 0; i < DEV_HASH_LEN; i++) {
+    eeprom_update(dev_fwhash_addr(i), dev_firmware_hash_target[i]);
+  }
+  if (!fw_signature_validated) hard_reset();
+}
+
+void device_validate_partitions() {
+  device_load_firmware_hash();
+  esp_partition_t partition;
+  partition.address   = ESP_PARTITION_TABLE_OFFSET;
+  partition.size      = ESP_PARTITION_TABLE_MAX_LEN;
+  partition.type      = ESP_PARTITION_TYPE_DATA;
+  esp_partition_get_sha256(&partition, dev_partition_table_hash);
+  partition.address   = ESP_BOOTLOADER_OFFSET;
+  partition.size      = ESP_PARTITION_TABLE_OFFSET;
+  partition.type      = ESP_PARTITION_TYPE_APP;
+  esp_partition_get_sha256(&partition, dev_bootloader_hash);
+  esp_partition_get_sha256(esp_ota_get_running_partition(), dev_firmware_hash);
+  for (uint8_t i = 0; i < DEV_HASH_LEN; i++) {
+    if (dev_firmware_hash_target[i] != dev_firmware_hash[i]) {
+      fw_signature_validated = false;
+      break;
+    }
+  }
+}
+
+bool device_firmware_ok() {
+  return fw_signature_validated;
+}
+
+bool device_init() {
+  if (bt_early_init()) {
+    for (uint8_t i=0; i<EEPROM_SIG_LEN; i++){dev_eeprom_signature[i]=EEPROM.read(eeprom_addr(ADDR_SIGNATURE+i));}
+    mbedtls_md_context_t ctx;
+    mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;     
+    mbedtls_md_init(&ctx);
+    mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0);
+    mbedtls_md_starts(&ctx);
+    mbedtls_md_update(&ctx, dev_bt_mac, BT_DEV_ADDR_LEN);
+    mbedtls_md_update(&ctx, dev_eeprom_signature, EEPROM_SIG_LEN);
+    mbedtls_md_finish(&ctx, dev_hash);
+    mbedtls_md_free(&ctx);
+    device_load_signature();
+    device_validate_signature();
+    device_validate_partitions();
+    device_init_done = true;
+    return device_init_done && fw_signature_validated;
+  } else {
+    return false;
+  }
+}
\ No newline at end of file
index 8bc485b9c923dbfc039fb7dd5bddd24fcd0f78c3..b8a8a1dcefa5d564d5ef7896b160abe79b96e048 100644 (file)
--- a/Framing.h
+++ b/Framing.h
@@ -32,6 +32,7 @@
        #define CMD_FB_READ     0x42
        #define CMD_FB_WRITE    0x43
        #define CMD_FB_READL    0x44
+       #define CMD_DISP_INT    0x45
        #define CMD_BT_CTRL     0x46
 
        #define CMD_BOARD               0x47
@@ -42,6 +43,9 @@
        #define CMD_ROM_WRITE   0x52
        #define CMD_CONF_SAVE   0x53
        #define CMD_CONF_DELETE 0x54
+       #define CMD_DEV_HASH    0x56
+       #define CMD_DEV_SIG     0x57
+       #define CMD_FW_HASH     0x58
        #define CMD_UNLOCK_ROM  0x59
        #define ROM_UNLOCK_BYTE 0xF8
        #define CMD_RESET       0x55
index 555ea86d7613f0a054d4706f5477f628e3711a38..f608859d4a68b8ff595a16a194c625c4af5d3318 100644 (file)
@@ -52,7 +52,8 @@ char sbuf[128];
 
 void setup() {
   #if MCU_VARIANT == MCU_ESP32
-    delay(500);
+    // TODO: Reset?
+    // delay(500);
     EEPROM.begin(EEPROM_SIZE);
     Serial.setRxBufferSize(CONFIG_UART_BUFFER_SIZE);
   #endif
@@ -75,7 +76,7 @@ void setup() {
 
   // Initialise buffers
   memset(pbuf, 0, sizeof(pbuf));
-  memset(cbuf, 0, sizeof(cbuf));
+  memset(cmdbuf, 0, sizeof(cmdbuf));
   
   memset(packet_queue, 0, sizeof(packet_queue));
 
@@ -102,7 +103,7 @@ void setup() {
   #endif
 
   // Validate board health, EEPROM and config
-  validateStatus();
+  validate_status();
 }
 
 void lora_receive() {
@@ -475,11 +476,11 @@ void serialCallback(uint8_t sbyte) {
                 if (sbyte == TFESC) sbyte = FESC;
                 ESCAPE = false;
             }
-            cbuf[frame_len++] = sbyte;
+            if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
         }
 
         if (frame_len == 4) {
-          uint32_t freq = (uint32_t)cbuf[0] << 24 | (uint32_t)cbuf[1] << 16 | (uint32_t)cbuf[2] << 8 | (uint32_t)cbuf[3];
+          uint32_t freq = (uint32_t)cmdbuf[0] << 24 | (uint32_t)cmdbuf[1] << 16 | (uint32_t)cmdbuf[2] << 8 | (uint32_t)cmdbuf[3];
 
           if (freq == 0) {
             kiss_indicate_frequency();
@@ -498,11 +499,11 @@ void serialCallback(uint8_t sbyte) {
                 if (sbyte == TFESC) sbyte = FESC;
                 ESCAPE = false;
             }
-            cbuf[frame_len++] = sbyte;
+            if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
         }
 
         if (frame_len == 4) {
-          uint32_t bw = (uint32_t)cbuf[0] << 24 | (uint32_t)cbuf[1] << 16 | (uint32_t)cbuf[2] << 8 | (uint32_t)cbuf[3];
+          uint32_t bw = (uint32_t)cmdbuf[0] << 24 | (uint32_t)cmdbuf[1] << 16 | (uint32_t)cmdbuf[2] << 8 | (uint32_t)cmdbuf[3];
 
           if (bw == 0) {
             kiss_indicate_bandwidth();
@@ -619,11 +620,11 @@ void serialCallback(uint8_t sbyte) {
                 if (sbyte == TFESC) sbyte = FESC;
                 ESCAPE = false;
             }
-            cbuf[frame_len++] = sbyte;
+            if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
         }
 
         if (frame_len == 2) {
-          eeprom_write(cbuf[0], cbuf[1]);
+          eeprom_write(cmdbuf[0], cmdbuf[1]);
         }
     } else if (command == CMD_FW_VERSION) {
       kiss_indicate_version();
@@ -658,20 +659,62 @@ void serialCallback(uint8_t sbyte) {
                 if (sbyte == TFESC) sbyte = FESC;
                 ESCAPE = false;
             }
-            cbuf[frame_len++] = sbyte;
+            if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
         }
         #if HAS_DISPLAY
           if (frame_len == 9) {
-            uint8_t line = cbuf[0];
+            uint8_t line = cmdbuf[0];
             if (line > 63) line = 63;
             int fb_o = line*8; 
-            memcpy(fb+fb_o, cbuf+1, 8);
+            memcpy(fb+fb_o, cmdbuf+1, 8);
           }
         #endif
     } else if (command == CMD_FB_READ) {
       if (sbyte != 0x00) {
         kiss_indicate_fb();
       }
+    } else if (command == CMD_DEV_HASH) {
+      #if MCU_VARIANT == MCU_ESP32
+        if (sbyte != 0x00) {
+          kiss_indicate_device_hash();
+        }
+      #endif
+    } else if (command == CMD_DEV_SIG) {
+      #if MCU_VARIANT == MCU_ESP32
+        if (sbyte == FESC) {
+              ESCAPE = true;
+          } else {
+              if (ESCAPE) {
+                  if (sbyte == TFEND) sbyte = FEND;
+                  if (sbyte == TFESC) sbyte = FESC;
+                  ESCAPE = false;
+              }
+              if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
+          }
+
+          if (frame_len == DEV_SIG_LEN) {
+            memcpy(dev_sig, cmdbuf, DEV_SIG_LEN);
+            device_save_signature();
+          }
+      #endif
+    } else if (command == CMD_FW_HASH) {
+      #if MCU_VARIANT == MCU_ESP32
+        if (sbyte == FESC) {
+              ESCAPE = true;
+          } else {
+              if (ESCAPE) {
+                  if (sbyte == TFEND) sbyte = FEND;
+                  if (sbyte == TFESC) sbyte = FESC;
+                  ESCAPE = false;
+              }
+              if (frame_len < CMD_L) cmdbuf[frame_len++] = sbyte;
+          }
+
+          if (frame_len == DEV_HASH_LEN) {
+            memcpy(dev_firmware_hash_target, cmdbuf, DEV_SIG_LEN);
+            device_save_firmware_hash();
+          }
+      #endif
     } else if (command == CMD_BT_CTRL) {
       #if HAS_BLUETOOTH
         if (sbyte == 0x00) {
@@ -684,6 +727,20 @@ void serialCallback(uint8_t sbyte) {
           bt_enable_pairing();
         }
       #endif
+    } else if (command == CMD_DISP_INT) {
+      #if HAS_DISPLAY
+        if (sbyte == FESC) {
+            ESCAPE = true;
+        } else {
+            if (ESCAPE) {
+                if (sbyte == TFEND) sbyte = FEND;
+                if (sbyte == TFESC) sbyte = FESC;
+                ESCAPE = false;
+            }
+            display_intensity = sbyte;
+        }
+
+      #endif
     }
   }
 }
@@ -739,7 +796,7 @@ void checkModemStatus() {
   }
 }
 
-void validateStatus() {
+void validate_status() {
   #if MCU_VARIANT == MCU_1284P
       uint8_t boot_flags = OPTIBOOT_MCUSR;
       uint8_t F_POR = PORF;
@@ -759,6 +816,15 @@ void validateStatus() {
       uint8_t F_WDR = 0x01;
   #endif
 
+  if (hw_ready || device_init_done) {
+    hw_ready = false;
+    Serial.write("Error, invalid hardware check state\r\n");
+    #if HAS_DISPLAY
+      if (disp_ready) update_display();
+    #endif
+    led_indicate_boot_error();
+  }
+
   if (boot_flags & (1<<F_POR)) {
     boot_vector = START_FROM_POWERON;
   } else if (boot_flags & (1<<F_BOR)) {
@@ -777,9 +843,17 @@ void validateStatus() {
     if (eeprom_lock_set()) {
       if (eeprom_product_valid() && eeprom_model_valid() && eeprom_hwrev_valid()) {
         if (eeprom_checksum_valid()) {
-          hw_ready = true;
+          #if PLATFORM == PLATFORM_ESP32
+            if (device_init()) {
+              hw_ready = true;
+            } else {
+              hw_ready = false;
+            }
+          #else
+            hw_ready = true;
+          #endif
 
-          if (eeprom_have_conf()) {
+          if (hw_ready && eeprom_have_conf()) {
             eeprom_conf_load();
             op_mode = MODE_TNC;
             startRadio();
index 376baf8925e5ec18f103c35747890475055b46af..3b10b779f8f7e050cf658ca82c64a7c210e49b3d 100644 (file)
@@ -19,6 +19,7 @@
 #endif
 
 #if MCU_VARIANT == MCU_ESP32
+       #include "Device.h"
   #include "soc/rtc_wdt.h"
   #define ISR_VECT IRAM_ATTR
 #else
@@ -296,7 +297,7 @@ unsigned long led_standby_ticks = 0;
 
 #elif MCU_VARIANT == MCU_ESP32
 
-       #if BOARD_MODEL == BOARD_RNODE_NG_20
+       #if BOARD_MODEL == BOARD_RNODE_NG_20 || BOARD_MODEL == BOARD_RNODE_NG_21
                uint8_t led_standby_lng = 32;
                uint8_t led_standby_cut = 16;
                uint8_t led_standby_min = 0;
@@ -307,7 +308,7 @@ unsigned long led_standby_ticks = 0;
                int8_t  led_notready_direction = 0;
                unsigned long led_notready_ticks = 0;
                unsigned long led_standby_wait = 1000;
-               unsigned long led_notready_wait = 20000;
+               unsigned long led_notready_wait = 200;
        
        #else
                uint8_t led_standby_min = 200;
@@ -416,22 +417,29 @@ int8_t  led_standby_direction = 0;
                }
        }
 #elif MCU_VARIANT == MCU_ESP32
-       #if BOARD_MODEL == BOARD_RNODE_NG_20
+       #if BOARD_MODEL == BOARD_RNODE_NG_20 || BOARD_MODEL == BOARD_RNODE_NG_21
     void led_indicate_not_ready() {
-                       led_notready_ticks++;
-                       if (led_notready_ticks > led_notready_wait) {
-                               led_notready_ticks = 0;
-                               if (led_notready_value <= led_notready_min) {
-                                       led_notready_direction = 1;
-                               } else if (led_notready_value >= led_notready_max) {
-                                       led_notready_direction = -1;
+       led_standby_ticks++;
+
+                       if (led_standby_ticks > led_notready_wait) {
+                               led_standby_ticks = 0;
+                               
+                               if (led_standby_value <= led_standby_min) {
+                                       led_standby_direction = 1;
+                               } else if (led_standby_value >= led_standby_max) {
+                                       led_standby_direction = -1;
                                }
-                               led_notready_value += led_notready_direction;
-                               if (led_notready_value > 252) {
-                                       npset(0xFF, 0x00, 0x00);
+
+                               led_standby_value += led_standby_direction;
+                               int offset = led_standby_value - led_standby_lng;
+                               uint8_t led_standby_intensity;
+                               if (offset < 0) {
+                               led_standby_intensity = 0;
                                } else {
-                                       npset(0x00, 0x00, 0x00);
+                               led_standby_intensity = offset;
                                }
+                               if (offset > led_standby_cut) offset = led_standby_cut;
+                       npset(led_standby_intensity, 0x00, 0x00);
                        }
                }
        #else
@@ -478,7 +486,7 @@ void serial_write(uint8_t byte) {
        #endif
 }
 
-void escapedSerialWrite(uint8_t byte) {
+void escaped_serial_write(uint8_t byte) {
        if (byte == FEND) { serial_write(FESC); byte = TFEND; }
     if (byte == FESC) { serial_write(FESC); byte = TFESC; }
     serial_write(byte);
@@ -508,20 +516,20 @@ void kiss_indicate_radiostate() {
 void kiss_indicate_stat_rx() {
        serial_write(FEND);
        serial_write(CMD_STAT_RX);
-       escapedSerialWrite(stat_rx>>24);
-       escapedSerialWrite(stat_rx>>16);
-       escapedSerialWrite(stat_rx>>8);
-       escapedSerialWrite(stat_rx);
+       escaped_serial_write(stat_rx>>24);
+       escaped_serial_write(stat_rx>>16);
+       escaped_serial_write(stat_rx>>8);
+       escaped_serial_write(stat_rx);
        serial_write(FEND);
 }
 
 void kiss_indicate_stat_tx() {
        serial_write(FEND);
        serial_write(CMD_STAT_TX);
-       escapedSerialWrite(stat_tx>>24);
-       escapedSerialWrite(stat_tx>>16);
-       escapedSerialWrite(stat_tx>>8);
-       escapedSerialWrite(stat_tx);
+       escaped_serial_write(stat_tx>>24);
+       escaped_serial_write(stat_tx>>16);
+       escaped_serial_write(stat_tx>>8);
+       escaped_serial_write(stat_tx);
        serial_write(FEND);
 }
 
@@ -529,14 +537,14 @@ void kiss_indicate_stat_rssi() {
        uint8_t packet_rssi_val = (uint8_t)(last_rssi+rssi_offset);
        serial_write(FEND);
        serial_write(CMD_STAT_RSSI);
-       escapedSerialWrite(packet_rssi_val);
+       escaped_serial_write(packet_rssi_val);
        serial_write(FEND);
 }
 
 void kiss_indicate_stat_snr() {
        serial_write(FEND);
        serial_write(CMD_STAT_SNR);
-       escapedSerialWrite(last_snr_raw);
+       escaped_serial_write(last_snr_raw);
        serial_write(FEND);
 }
 
@@ -578,20 +586,20 @@ void kiss_indicate_txpower() {
 void kiss_indicate_bandwidth() {
        serial_write(FEND);
        serial_write(CMD_BANDWIDTH);
-       escapedSerialWrite(lora_bw>>24);
-       escapedSerialWrite(lora_bw>>16);
-       escapedSerialWrite(lora_bw>>8);
-       escapedSerialWrite(lora_bw);
+       escaped_serial_write(lora_bw>>24);
+       escaped_serial_write(lora_bw>>16);
+       escaped_serial_write(lora_bw>>8);
+       escaped_serial_write(lora_bw);
        serial_write(FEND);
 }
 
 void kiss_indicate_frequency() {
        serial_write(FEND);
        serial_write(CMD_FREQUENCY);
-       escapedSerialWrite(lora_freq>>24);
-       escapedSerialWrite(lora_freq>>16);
-       escapedSerialWrite(lora_freq>>8);
-       escapedSerialWrite(lora_freq);
+       escaped_serial_write(lora_freq>>24);
+       escaped_serial_write(lora_freq>>16);
+       escaped_serial_write(lora_freq>>8);
+       escaped_serial_write(lora_freq);
        serial_write(FEND);
 }
 
@@ -617,15 +625,25 @@ void kiss_indicate_fbstate() {
        serial_write(FEND);
 }
 
+#if MCU_VARIANT == MCU_ESP32
+       void kiss_indicate_device_hash() {
+         serial_write(FEND);
+         serial_write(CMD_DEV_HASH);
+         for (int i = 0; i < DEV_HASH_LEN; i++) {
+           uint8_t byte = dev_hash[i];
+                       escaped_serial_write(byte);
+         }
+         serial_write(FEND);
+       }
+#endif
+
 void kiss_indicate_fb() {
        serial_write(FEND);
        serial_write(CMD_FB_READ);
        #if HAS_DISPLAY
                for (int i = 0; i < 512; i++) {
                        uint8_t byte = fb[i];
-                       if (byte == FEND) { serial_write(FESC); byte = TFEND; }
-                       if (byte == FESC) { serial_write(FESC); byte = TFESC; }
-                       serial_write(byte);
+                       escaped_serial_write(byte);
                }
        #else
                serial_write(0xFF);
@@ -799,21 +817,21 @@ bool eeprom_info_locked() {
 void eeprom_dump_info() {
        for (int addr = ADDR_PRODUCT; addr <= ADDR_INFO_LOCK; addr++) {
                uint8_t byte = EEPROM.read(eeprom_addr(addr));
-               escapedSerialWrite(byte);
+               escaped_serial_write(byte);
        }
 }
 
 void eeprom_dump_config() {
        for (int addr = ADDR_CONF_SF; addr <= ADDR_CONF_OK; addr++) {
                uint8_t byte = EEPROM.read(eeprom_addr(addr));
-               escapedSerialWrite(byte);
+               escaped_serial_write(byte);
        }
 }
 
 void eeprom_dump_all() {
        for (int addr = 0; addr < EEPROM_RESERVED; addr++) {
                uint8_t byte = EEPROM.read(eeprom_addr(addr));
-               escapedSerialWrite(byte);
+               escaped_serial_write(byte);
        }
 }