Skip to content

Bootloader Secure Boot

Secure boot is a feature that ensures that only cryptographically authorized PX4 firmware is executed. This is used by OEMs to ensure that only their validated, tested firmware runs on the vehicle — protecting safety, brand integrity, and regulatory compliance, and stopping others from running unauthorized software on their hardware.

The PX4 Bootloader can verify a cryptographic signature over the PX4 firmware before it is run. When enabled, only a firmware image signed with a private key whose matching public key is baked into the bootloader will boot. For any unsigned image, tampered image, or image signed by the wrong key, the device will wait in the bootloader screen (where it can be recovered safely over USB), and will not start the rest of the PX4 flight stack.

WARNING

This feature is intended for OEMs.

If you flash a bootloader that trusts a public key whose private counterpart you have lost, you can no longer sign new firmware for that device and will need a debug probe to recover it. Keep private keys backed up and never commit them to source control.

How It Works

PX4's secure boot uses ed25519 signatures (via monocypher). The signing key is 32 bytes and the resulting signature is 64 bytes. Verification is fast (no random number generator is required on the device), and the implementation is small enough to fit in a 128 KB bootloader sector alongside the rest of the bootloader.

A signed firmware image lays out in flash like this:

txt
+---------------------------+  APP_LOAD_ADDRESS              ─┐
| Vector table              |                                 │
+---------------------------+  APP_LOAD_ADDRESS               │
| Image TOC                 |    + BOARD_IMAGE_TOC_OFFSET     │ BOOT region
+---------------------------+                                 │ (hashed and signed)
| .text / .rodata / .data   |                                 │
+---------------------------+  &_boot_signature              ─┤
| 64-byte ed25519 signature |                                 │ SIG1
+---------------------------+                                ─┘

The Table of Contents (TOC) is a small data structure compiled into the firmware that tells the bootloader which region to hash and which key slot to verify against. Its format is defined in src/include/image_toc.h. For px4_fmu-v6x the TOC declares two entries: BOOT (the firmware bytes to verify) and SIG1 (the 64-byte ed25519 signature to verify them against).

On reset the bootloader reads the TOC at a fixed offset, computes an ed25519 signature over the BOOT region, and compares it to the SIG1 entry. If the signature verifies it jumps to the app; otherwise it stays in the bootloader and waits for a new upload.

The host-side uploader (Tools/px4_uploader.py) also asks the bootloader to verify the signature before sending the final reboot, via a dedicated VERIFY_SIG opcode. This means a signature mismatch is reported as a clean error from px_uploader.py instead of a silent "device stays in bootloader after reboot".

Trying It Out

PX4 ships a secure-boot example for px4_fmu-v6x. This consists of two build variants:

  • px4_fmu-v6x_bootloader_secureboot — the secure bootloader, with ed25519 verification enabled, and the upstream test public key baked in.
  • px4_fmu-v6x_secureboot — PX4 firmware, with TOC and automatic signing.

The steps are:

  1. Build and flash the secure bootloader (one-time, via SWD)

    • Build the bootloader:

      sh
      make px4_fmu-v6x_bootloader_secureboot
    • Flash the resulting build/px4_fmu-v6x_bootloader_secureboot/px4_fmu-v6x_bootloader_secureboot.elf via a debug probe. See Bootloader Update.

      TIP

      This is the only step that needs SWD — once the secure bootloader is in place, all firmware updates go over USB.

  2. Build and upload signed firmware

    sh
    make px4_fmu-v6x_secureboot upload

    The build produces an unsigned .bin, signs it with the upstream test key (Tools/test_keys/test_keys.json), wraps it in a .px4 envelope marked image_signed: true, and uploads it. You will see something like:

    sh
    Verify   ▕██████████████████████████████▏ 100%
    Verifying image signature... passed
    Uploaded in 18s

If you upload an image that the bootloader can't verify (e.g. an unsigned .px4, a tampered .bin, or one signed with a different key), px_uploader.py reports the failure before reboot:

sh
Upload failed: Signature verification failed: image will not boot.
The bootloader computed a signature over the flashed image that does not
match any public key it trusts.

Generating Your Own Keys

The default test key is committed to the PX4 tree, so a real deployment must replace it (in order to protect the public key). Generate a new ed25519 key pair with:

shell
python3 Tools/secure_bootloader/generate_signing_keys.py /path/to/my_key

This writes:

  • my_key.json — the private key used by sign_firmware.py. Keep this private. Do not commit it.
  • my_key.pub — the public key as a C-array fragment, suitable for #include in the bootloader's keystore.

Make the following changes use your new keys in the build:

  1. Update the bootloader to trust your public key.

    Edit boards/px4/fmu-v6x/bootloader_secureboot.px4board and change CONFIG_PUBLIC_KEY0 to point at my_key.pub. Rebuild and reflash the bootloader via SWD.

  2. Tell the app build to sign with the matching private key.

    Either edit boards/px4/fmu-v6x/secureboot.px4board and change CONFIG_BOARD_SECUREBOOT_KEY to point at my_key.json, or set the environment variable at build time:

    sh
    BOARD_SECUREBOOT_KEY=/path/to/my_key.json make px4_fmu-v6x_secureboot upload

The keys used must always be the corresponding cryptographic pair — if you flash a bootloader that trusts a key whose private half you don't have, you can only recover via SWD.

Enabling Secure Boot on a New Board

To enable secure boot up on a board that doesn't already have a secureboot variant, you'll need:

  • a toc.c file placed in the board's src/ (modeled on boards/px4/fmu-v6x/src/toc.c),
  • a linker script with _main_toc reserved at a fixed offset past the vector table and a .signature section at the end of FLASH (see boards/px4/fmu-v6x/nuttx-config/scripts/secureboot-script.ld)
  • a secureboot.px4board setting CONFIG_BOARD_SECUREBOOT=y and the linker prefix,
  • a bootloader_secureboot.px4board enabling CONFIG_BOARD_CRYPTO, CONFIG_DRIVERS_SW_CRYPTO, CONFIG_DRIVERS_STUB_KEYSTORE and the public-key path,
  • BOOTLOADER_USE_SECURITY + BOOTLOADER_SIGNING_ALGORITHM + BOARD_IMAGE_TOC_OFFSET defines in the board's hw_config.h, gated on PX4_CRYPTO.

The fmu-v6x variant files are kept small and self-contained for exactly this reason — they are intended to be copied as a starting point.

See Also