usedbytes / picowota

A bootloader for OTA WiFi code upload to a Raspberry Pi Pico W
BSD 3-Clause "New" or "Revised" License
114 stars 21 forks source link

Authentication #6

Open felixdoerre opened 1 year ago

felixdoerre commented 1 year ago

Hi there,

This is another issue where I already have a hacky solution and I wanted to get feedback if it is worth cleaning up and creating a pull request:

Right now, anyone in the local net who knows the target ip can down- and upload arbitrary firmware if the pico is in the bootloader.

I locally implemented authentication for both the connection as such, and in particular the firmware. Right now these changes are incompatible with the current serial-flasher protocol, and we could think about how to make them compatible, even if only compatible between a version of picowota where authentication is enabled at compile time and one where it isn't.

In terms of size, the authentication seems to add 3k from the cryptography and a small bit for changes to the logic. This results in a total bootloader size of 300k (station mode, 227k wlan firmware incl. bluetooth for sharing with micropython).

Regarding the security guarantees:

Is this something you would want to merge?

usedbytes commented 11 months ago

Thanks for all the contributions - I will do my best to find time to work through them. This project started out as a bare-minimum attempt to get OTA working on the Pico, but in principle I'd be happy to expand it to something wider/more-capable.

I wouldn't necessarily mind making a breaking change to the protocol. The current protocol is largely driven by my older non-WiFi bootloader, which had some somewhat restrictive requirements imposed by the system architecture (https://blog.usedbytes.com/2021/12/esp32-wifi/bluetooth-bridge/).

My priorities for building something more capable would be:

  1. Unify the serial and WiFi code-bases
  2. Improve the build system so that the whole flash size, image generation and image offset address handling is less hacky
  3. This kind of enhancement for security (honestly my threat model doesn't much care about bad actors - but I get that not everyone has that luxury, and I've heard from a few companies who either are, or want to, use this code)

Of course, my priorities don't have to align with yours 😄 To merge this, it would be good to have a build option to keep the "old" protocol, and/or a runtime handshake to fall back to the old protocol (the 3kB doesn't sound like a dealbreaker when you consider the WiFi firmware). Also your TODO in the linker script - any reason not to copy everything (except the FW image) to RAM?

BTW:

227k wlan firmware incl. bluetooth for sharing with micropython

How do you share the firmware with the app? Did the SDK add support for this, or you've patched it in?

felixdoerre commented 11 months ago

The "TODO" is actually not my todo, but taken from here: https://github.com/micropython/micropython/blob/516385c4ccbb1f7fa9dbd37a44ed8ae33dcf1942/ports/rp2/memmap_mp.ld#L67 I think eventually we should figure out what the best/most clean linker script would be. So probably that's also the reason why that script is sparse with ram, even though it should not.

Regarding priorities: I'm not that much interested in the serial bootloader, I maybe have a solution to get the build system less hacky and I personally don't strictly "need" the security but it just feels wrong to not have it for devices in the network.

So... regarding sharing firmware: I still have some changes locally that I haven't mold into a PR, to pass around offsets better and to share the firmware, let me give a brief overview here:

I build picowota into an elf file regularly, with the layout (image/header location, coming from the linker script through linker-script defined symbols). I use a "special" linker command to extract some symbols out of picowota.elf into picowota.int, defining the interface:

${CMAKE_LINKER} -r -o picowota.int -R picowota.elf --retain-symbols-file ${CMAKE_CURRENT_LIST_DIR}/retain_symbols.txt

This interface file now contains all symbols from the bootloader that are to be exported for the application to link against. In my case I chose:

wb43439A0_7_95_49_00_combined
__wota_image_header_offset
picowota_reboot

For linking micropython (as a picowota app), I use an adapted linker script that uses these exported symbols:

INPUT(absolute/path/to/picowota.int);
.....
    .flash_begin : {
        . = __wota_image_header_offset - 0x10000000 + 4k;
        __flash_binary_start = .;
    } > FLASH

I can even declare picowota_reboot as an external function and just use the function defined in the bootloader. Now the only thing that remains is, including picowota.int in the linker command and convincing the pico-sdk to drop its wb43439A0_7_95_49_00_combined symbol. Locally I've done that by replacing the data definition with an external variable. Replacing the whole firmware header seems possible through CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE, however that would duplicate the macros indicating the firmware length. And as they are macros they can't easily be inserted by the linker. Just swapping out the symbol in the linker is probably possible, but I didn't look into that any further for now.