espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.72k stars 7.43k forks source link

"Decryption error" on update via OTA with pre-encrypted file #9944

Closed Mikolaj-AND closed 4 months ago

Mikolaj-AND commented 4 months ago

Board

ESP32-WROOM-32

Device Description

ESP32-WROOM-32 DevkitC

Hardware Configuration

Bare board powered by USB cable

Version

latest master (checkout manually)

IDE Name

Arduino IDE 2.3.2

Operating System

Windows 10

Flash frequency

80MHz

PSRAM enabled

no

Upload speed

115200

Description

I've generated flash encryption key: _espefuse.py generate_flash_encryption_key flash_encryptionkey.bin

Then burned it with required efuses.

Flashed pre-encrypted bootloader and partition table

Then generated binary with OTA capability. Uploaded the .bin file at address 0x10000 with esptool.py.

OTA works fine with not-encrypted .bin from Arduino IDE. But after encrypting the file with _espsecure.py encrypt_flashdata, I'm getting multiple "Decryption error" messages on COM port during OTA process. The encrypted file is correct, as I've decrypted it back to a regular .bin file using _espsecure.py decrypt_flashdata and it's the same as original .bin file.

Encryption is enabled, as I'm getting "Serial.println("Encryption Enabled");" printed on setup.

Sketch

#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>

const char* host = "esp32";

#include "esp_flash_encrypt.h"

String serverIndex = R"====(
<html>
<head>
  <style>
    body {
      font-family: Arial, sans-serif;
      display: flex;
      flex-direction: column; /* Stack elements vertically */
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      margin: 0;
      background-color: #f0f9ff;
    }

    #upload_form {
      background-color: #fff;
      border-radius: 5px;
      padding: 20px;
      box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
      margin-bottom: 10px; /* Optional spacing */
    }

    .btn {
      background-color: #007bff;
      color: #fff;
      border: none;
      border-radius: 5px;
      padding: 10px 20px;
      font-size: 16px;
      cursor: pointer;
      transition: background-color 0.2s ease-in-out;
    }

    .btn:hover {
      background-color: #0069d9;
    }

    #progress-bar {
      width: 350px;
      height: 10px;
      background-color: #ddd;
      border-radius: 5px;
    }

    #progress-bar > div {
      height: 100%;
      background-color: #ddd;
      border-radius: inherit;
      transition: width 0.2s ease-in-out;
    }

    input[type="file"] {
      opacity: 0;
      width: 0.1px;
      height: 0.1px;
      position: absolute;
    }

    .custom-file-upload {
      background-color: grey; /* Same as your button background */
      color: #fff;
      border: none;
      border-radius: 5px; /* Same as your button border-radius */
      padding: 10px 20px;
      font-size: 16px;
      cursor: pointer;
      transition: background-color 0.2s ease-in-out;
      display: inline-block; /* Make it behave like a button */
      margin-right: 20px;
    }

    .custom-file-upload:hover {
      background-color: #0069d9; /* Same as your button hover color */
    }
  </style>
</head>
<body>
  <form method='POST' action='/update' enctype='multipart/form-data' id='upload_form'>
    <label for="update" class="custom-file-upload">Wybierz plik...</label>
    <input type="file" id="update" name="update">

    <input type='submit' value='Update' class='btn'><br><br>
     <label id="file-name-label" style="font-size:12px; color:green; margin-top:10px; padding-bottom:5px;"></label>
  </form>
  <div id="progress-bar">
    <div></div>
  </div>
  <script>
   var fileInput = document.getElementById('update');
    var fileNameDisplay = document.getElementById('file-name-label');

    fileInput.addEventListener('change', function() {
      var fileName = fileInput.files[0].name;
      fileNameDisplay.textContent = fileName;
    });

    var form = document.getElementById('upload_form');
var progressBar = document.getElementById('progress-bar').firstElementChild;
var progressBarWidth = 0; // initialize the progress bar width to 0

form.addEventListener('submit', function(e) {
  e.preventDefault();
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/update', true);
  xhr.upload.addEventListener('progress', function(evt) {
    if (evt.lengthComputable) {
      var per = evt.loaded / evt.total;
      progressBarWidth = per * 100; // calculate the progress percentage
      progressBar.style.width = progressBarWidth + '%'; // update the progress bar width
      progressBar.style.background = 'linear-gradient(to right, #007bff ' + progressBarWidth + '%, #ddd ' + progressBarWidth + '%)'; // update the progress bar color
      progressBar.style.transition = 'width 0.2s ease-in-out'; // add animation
    }
  }, false);
  xhr.send(new FormData(form));
  xhr.onload = function() {
    if (xhr.status === 200) {
      console.log('success!');
    }
  };
  xhr.onerror = function() {
    // handle error
  };
});
  </script>
</body>
</html>

)====";
WebServer server(80);
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  WiFi.softAP("esp32_ota", "password", 1, 0, 4);

  server.on("/ota", HTTP_GET, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", serverIndex);
  });
  /*handling uploading firmware file */
  server.on("/update", HTTP_POST, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
    ESP.restart();
  }, []() {
    HTTPUpload& upload = server.upload();
    if (upload.status == UPLOAD_FILE_START) {
      Serial.printf("Update: %s\n", upload.filename.c_str());
      if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_WRITE) {
      /* flashing firmware to ESP*/
      if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_END) {
      if (Update.end(true)) { //true to set the size to the current progress
        Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
      } else {
        Update.printError(Serial);
      }
    }
  });
  server.begin();
  Serial.println("HTTP server started");

  if (esp_flash_encryption_enabled())
    Serial.println("Encryption Enabled");
  else
    Serial.println("Encryption not Enabled");

}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.print("Hello world 1\n");
  delay(500);
  server.handleClient();
}

Debug Message

Update: ota_firmware_encrypted.bin<\n>Decryption error<\r>
<\n>
Decryption error<\r>
<\n>
Decryption error

etc

Other Steps to Reproduce

No response

I have checked existing issues, online documentation and the Troubleshooting Guide

me-no-dev commented 4 months ago

We don't officially support firmware encryption in Arduino. Everything you see is community developed and supported. As a way to move forward, I'll suggest you look into IDF's examples and replace Update with that.

Mikolaj-AND commented 4 months ago

Thanks. Current workaround is to just use example from Update library https://github.com/espressif/arduino-esp32/tree/master/libraries/Update/examples/HTTP_Server_AES_OTA_Update and set const uint32_t OTA_MODE = U_AES_DECRYPT_ON; Of course the example does not make use of the ESP's encryption key and uses it's own predefined key instead.