hardwallet-bootloader/PRODUCTION.md

5.9 KiB

Hardware Wallet Bootloader

To use the bootloader effectively there a few steps that need to be taken. This document goes through all phases and the scripts used.

Notes for development

During development the bootloader will be loaded on chip using the ST-LINK programmer without write and read protection. The private keys used to sign the firmware can be commited to the repository, since these will not be used in the released bootloader. The keys will be generated by the development team with no confidentiality constraints. Do not rely on these keys for anything remotely security related. To make sure the space occupied by the bootloader and boot times are the same, we will use the same number signatures both in development and in release.

Loading the firmware in any of the reserved areas (firmware, recovery, upgrade) can be performed via the fw_load.sh script. This script is only applicable to development and is not used in production.

Using the Python scripts

The Python scripts depend on the ecdsa module. It must be installed system-wide using your favourite python package manager.

Generating the keys

The most important step for generating the keys is to determine who is responsible for holding them and how many signatures will be needed for release. A good number would be 3 or 4, but the header can fit up to 6 signatures. The key holders are responsible for keeping the keys secret. If all keys were to be eventually leaked, attackers would be able to load their own malicious firmware on the device, so secrecy of the keys is crucial.

The private and public keys are generated using the genkeys.py script in the scripts folder. It takes no argument and generates two files: secret_key.pem and public_key.h. The content of the public_key.h file must be integrated in the main.c file.

The secret_key.pem is the one used for signing and is confidential. It is suggested to execute the script on a secure computer (no internet access, maybe a VM).

Beside the key file, the key holder will have an assigned index. This index is simply the order in which their signature appears in the header. This order is fixed and assignment is not done by technical means, but decided by the company.

TODO: The genkeys.py MUST encrypt the private key and the fw_sign.py (the one used to sign) must be able to decrypt it. We might use standard PEM encryption or just encrypt the PEM string before writing to disk. Before this task is completed only development keys can be generated

Loading the bootloader

The bootloader must be loaded using the ST-LINK programmer or something compatible. In production a single .bin file containing the bootloader, the factory-loaded firmware and the recovery firmware (which will be the same) must be used in order to load the initial flash content at once. The first boot must be executed immediately after loading, since at this stage the bootloader will set the write protection options on its pages and the pages occupied by the recovery firmware. It also activates the Read Out Protection Level 2 which prevents dumping the content of the flash, overwriting the option bytes (which control write protection) and disable all debugging interfaces. At this point the MCU will be secured and ready for shipping. The firmware and recovery firmware must also be signed exactly as it is done for firmware upgrades. The details on how to do this are in the next section.

A simple script to generate the final .bin file from the bootloader and the firmware bin files will be provided.

Issuing firmware upgrades

There are several steps needed when issuing a firmware upgrade

  1. The firmware must be built from its sources. The raw bin file needs to be copied in the scripts directory with the name fw.bin
  2. The fw_prepare.py script must be executed. This adds the header to the firmware, with no signatures.
  3. The file must be then signed, using the fw_sign.py script by all key holders. This scripts takes as argument the index of the key. It looks for a file named secret_key_index.pem where index is the argumenet passed to the script. This naming convention prevents passing the wrong index by mistake.
  4. The firmware is released (by means outside the scope of this document).

Since multiple signatures are required, we must decide how to go about that. The simplest and slowest option is having a fixed signing order (matching the index number of the keys), where each key holder signs and sends to the next person in the chain. The last person in the chain releases the firmware (or sends the firmware to the team responsible for releasing it). Another option is placing the unsigned firmware on a shared network driver and have all key holders sign in parallel. In this case locking must be performed to avoid concurrency issues and also the tool must inform the last signer that the firmware is ready. The third option is send the file to all signers immediately, who send their partially signed version back to the person initiaing the signing request. In this case another script will be needed to merge all partial headers into one, containing all signatures.

Update procedure

The update procedure goes as follows

  1. The client application (running on mobile or desktop) finds the updated firmware (by mechanisms outside the scope of this document).
  2. The client uploads the new firmware to the device. Note that during this step the device's current firmware and not the bootloader is active. The device stores the upgrade in the dedicated upgrade area, then verifies its signatures. If the signatures are invalid it sends an error response back to the client and/or shows an error on its display as well as erasing the upgrade area. This step is needed for UX reasons, since the bootloader does not display error messages. If the new firmware is valid, the device resets itself (software reset).
  3. The bootloader takes over and performs the upgrade procedure as described in the README.md document.