Garage Door Opener – Modifying the ESP8266 Over-the-Air update code
Now that I have the proof of concept code running, it’s time to modify the built in Arduino core library that handles OTA updates.
The existing OTA library takes a binary object and an optional MD5 hash (to verify the upload), stores it in flash memory, then swaps the old binary out of the new binary and reboots the device.
To do verification via digital signatures, we need three additional pieces of information: the developers certificate (used to decrypt the hash), the encrypted hash, and the Certificate Authority certificate used to verify the developers signature.
The CA needs to be compiled in to the source code – there is little point sending that along with our payload.
The developer certificate and encrypted hash on the other hand, need to be supplied with the binary . One option is to upload the three files separately, but this would require extensive reworking of the updater API, and of the OTA libraries.
A better option would be to somehow bundle all three files in one package, which is the path I am looking to go down.
So, the first thing to do is work out what the file format looks like.
The binary blob is of an arbitrary size, and starts with the magic byte: 0xE9, which I assume is an instruction that is required to start the boot process.
Our certificate is also an arbitrary size. The signature will be fixed size, but dependent on the algorithm we use. Clearly we need some way of instructing the updater code where the boundaries for each file are.
We could pack them at the beginning, and set the byte before the file with the expected length – ie if our signature was four bytes, and certificate was 6 bytes it might look like this:
[ 4 | s | s | s | s | 6 | c | c | c | c | c | c | … ]
but that would mean we’d have to move the data around, as the bootloader would be looking for the magic number in the position 0. I’ve decided to do it the other way around – I’m going to use the last two bits to signify the lengths, and then count backwards. ie:
[ … | c | c | c | c | c | c | s | s | s | s | 6 | 4 ]
I wrote a quick little c program that packages everything up.
#include <stdio.h>
#include <stdlib.h>
unsigned char *bundle;
uint32_t size;
uint32_t certificate_size;
uint32_t signature_size;
int main() {
FILE *f1 = fopen("WebUpdater.ino.bin", "rb");
if(f1) {
fseek(f1, 0, SEEK_END);
size = ftell(f1);
rewind(f1);
printf("Binary file size: %i\n", size);
} else {
printf("Unable to open WebUpdater.ino.bin\n");
return -1;
}
FILE *f2 = fopen("developer.crt.der", "rb");
if(f2) {
fseek(f2, 0, SEEK_END);
certificate_size = ftell(f2);
rewind(f2);
printf("Certificate size: %i\n", certificate_size);
} else {
printf("Unable to open developer.crt.der\n");
return -1;
}
FILE *f3 = fopen("WebUpdater.ino.sig", "rb");
if(f3) {
fseek(f3, 0, SEEK_END);
signature_size = ftell(f3);
rewind(f3);
printf("Signature size: %i\n", signature_size);
} else {
printf("Unable to open WebUpdater.ino.sig\n");
return -1;
}
printf("Signature size: 0x%x\n", signature_size);
uint32_t bundle_size = size + certificate_size + signature_size + (2 * sizeof(uint32_t));
bundle = (unsigned char *)malloc(bundle_size);
for(int i = 0; i < bundle_size; i++) {
bundle[i] = 0;
}
fread(bundle, size, 1, f1);
fread(bundle + size, certificate_size, 1, f2);
fread(bundle + size + certificate_size, signature_size, 1, f3);
bundle[bundle_size - 4] = signature_size & 0xFF;
bundle[bundle_size - 3] = (signature_size >> 8) & 0xFF;
bundle[bundle_size - 2] = (signature_size >> 16) & 0xFF;
bundle[bundle_size - 1] = (signature_size >> 24) & 0xFF;
bundle[bundle_size - 8] = certificate_size & 0xFF;
bundle[bundle_size - 7] = (certificate_size >> 8) & 0xFF;
bundle[bundle_size - 6] = (certificate_size >> 16) & 0xFF;
bundle[bundle_size - 5] = (certificate_size >> 24) & 0xFF;
FILE *f4 = fopen("Bundle.bin", "wb");
if(f4) {
fwrite(bundle, bundle_size, 1, f4);
printf("Bundle size: %i\n", bundle_size);
} else {
printf("Unable to save Bundle.bin");
}
return 0;
}
This produces a Bundle.bin file that can be uploaded.So far, I’ve managed to decode the lengths, and find where the two files I’m interested are live. Next I need to pull the files out, and do the verification. I think I’ll sign the binary using MD5 for the moment, as the updater class already has that function built in, so I effectively get it for free.