0

Disclaimer: It may be that this question belongs to a PHP forum instead of this one. However, this question is also very ESP32/Arduino related. Please correct me if this is the case.

The scenario:

  • Implementing self-update routines for my ESP32 project, using HTTPUpdate Using a server-side PHP script to decide which binary image to serve to the device, or return HTTP 304 return code if no updates available.
  • Using the sketch's MD5 and SHA-256 hashes provided by the HTTPUpdate library as request headers, server-side, to check binary version.

The problem:

  • The MD5 hash contained in the request header "x-ESP32-sketch-md5" is the same as the MD5 hash calculated by a simple md5() call using the raw binary for the currently running version.
  • The SHA256 hash contained in the request header "x-ESP32-sketch-sha256" is not the same as the hash obtained by calling PHP's hash("sha256", $v) (where $v contains the same raw binary).

Example:

  • MD5 (HTTPUpdate): c93d814989b53a24a0164e561b6a8bf8
  • MD5 (PHP): c93d814989b53a24a0164e561b6a8bf8 (no difference)
  • SHA256 (HTTPUpdate): 5634674253E095600CD863A34287FCA742BCFCF9B5BF409870DD65AF0ADEF7A0
  • SHA256 (PHP): 1ECDDF3215520C79AAD13BBC1D116D9286910490DF316B9B203FCBFC89CBCCC2 (different)

Am I doing something wrong?

Note: The esp_partition_get_sha256() function is called internally by the update() method of the HTTPUpdate library.

PHP Code (for calculating the hashes):

<?php

$v = file_get_contents("fw.bin");

print("MD5: ". md5($v)."\r\n");
print("SHA256: ".hash("sha256", $v)."\r\n");

Arduino code (the relevant part):

// Called once or twice a day
void update_fw()
{
    Serial.println("Checking for updates...");

    t_httpUpdate_return ret = httpUpdate.update(client, "http://my_server/fw/check_fw.php");  // For now it only logs the headers and returns HTTP 304 code

    switch (ret)
    {
      case HTTP_UPDATE_FAILED:
        Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
        break;

      case HTTP_UPDATE_NO_UPDATES:
        Serial.println("HTTP_UPDATE_NO_UPDATES");
        break;

      case HTTP_UPDATE_OK:
        Serial.println("HTTP_UPDATE_OK");
        break;
    }
}

PHP code (logs headers and returns HTTP 304 code):

<?php

$headers = getallheaders();

if (!file_exists("log.txt"))
{
    file_put_contents("log.txt", print_r($headers, true));
}

http_response_code(304);            // No updates.

exit;

Thanks in advance.

1

Your problem stems from a misunderstanding of what the functions are doing.

Your MD5 hashes are the hash of the binary file itself. And so they match.

Your PHP SHA256 is the hash of the binary file (like the MD5), but the ESP's SHA256 is not.

The file itself contains an SHA256 hash of the content of the file, not the whole file. The function esp_partition_get_sha256 merely returns that SHA256 hash.

According to the SDK documentation:

For apps with SHA-256 appended to the app image, the result is the appended SHA-256 value for the app image content. The hash is verified before returning, if app content is invalid then the function returns ESP_ERR_IMAGE_INVALID. For apps without SHA-256 appended to the image, the result is the SHA-256 of all bytes in the app image. For other partition types, the result is the SHA-256 of the entire partition.

If you were to take the binary file and somehow strip off the extra sha256 information and then perform a hash of the results you should (theoretically) get the same hash, which would be the same has as is contained within the file itself.

  • True. I reached the same conclusion via a completely different way. Following a hunch, I opened the file in a hex editor and at once recognized familiar bytes at the end. Not something very scientific, but alas, there they were. While my research could've been more thorough, I partly blame it on the lack of in-code comments and documentation, and quite poor (not findable) documentation for the Arduino-ESP32 project. Maybe it's time for me to contribute more? Oh, and thank you. – Flyingfenix Oct 1 '19 at 13:51
  • To "somehow strip the extra SHA256": $image = substr($old_image,0, -32);. – Flyingfenix Oct 1 '19 at 13:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.