use multiple signatures, switch to low-power

This commit is contained in:
Michele Balistreri 2018-05-18 13:32:00 +03:00
parent 7fab503474
commit ba9a3ac70c
7 changed files with 33 additions and 15 deletions

View File

@ -33,11 +33,10 @@
// The constants below define the memory layout documented in the README.md file
#define BOOTLOADER_SIZE 0x2000
#define BOOTLOADER_FIRST_PAGE 0
#define BOOTLOADER_LAST_PAGE ((BOOTLOADER_SIZE / FLASH_PAGE_SIZE) - 1)
#define FIRMWARE_SIZE ((FLASH_BANK_SIZE - BOOTLOADER_SIZE) / 2)
#define FIRMWARE_HEADER_SIZE 0x200
#define FIRMWARE_FIRST_PAGE (BOOTLOADER_LAST_PAGE + 1)
#define FIRMWARE_FIRST_PAGE (BOOTLOADER_SIZE / FLASH_PAGE_SIZE)
#define FIRMWARE_PAGE_COUNT (FIRMWARE_SIZE / FLASH_PAGE_SIZE)
#define FIRMWARE_START (FLASH_START_BANK1 + BOOTLOADER_SIZE)
@ -48,12 +47,11 @@
#define RECOVERY_FW_START (FIRMWARE_START + FIRMWARE_SIZE)
#define RECOVERY_FW_FIRST_PAGE (FIRMWARE_FIRST_PAGE + FIRMWARE_PAGE_COUNT)
#define RECOVERY_FW_LAST_PAGE (RECOVERY_FW_FIRST_PAGE + FIRMWARE_PAGE_COUNT - 1)
#define SIGNATURE_HEADER_OFFSET 8
#define SIGNATURE_LENGTH 64
#define KEY_LENGTH SIGNATURE_LENGTH
#define SIGNATURE_COUNT 1
#define SIGNATURE_COUNT 4
/**

View File

@ -6,7 +6,7 @@ To use the bootloader effectively there a few steps that need to be taken. This
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.
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
@ -16,7 +16,7 @@ The Python scripts depend on the ecdsa module. It must be installed system-wide
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 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).
@ -35,8 +35,8 @@ A simple script to generate the final .bin file from the bootloader and the firm
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.
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.

View File

@ -28,8 +28,19 @@
#include "uECC.h"
// The public keys used to verify the firmware signatures. These are the raw X,Y coordinates. Each key is exactly 64 bytes long and there is no separator.
uint8_t fw_public_keys[] = { 0xfe, 0xcb, 0x28, 0xb9, 0x50, 0xdd, 0x8b, 0x2f, 0xc7, 0x34, 0xd3, 0x60, 0x5b, 0x1a, 0xc6, 0xed, 0x02, 0x50, 0xf2, 0x4a, 0xc4, 0x75, 0xd1, 0x28, 0x7f, 0x7c, 0xb5, 0xce, 0x61, 0xd6, 0x95, 0xb9, 0xb5, 0x27, 0x0b, 0x52, 0x77, 0x42, 0x4b, 0xf3, 0xb4, 0x3c, 0xef, 0xcb, 0x56, 0xd1, 0x98, 0x22, 0x11, 0xc2, 0xe5, 0xd3, 0xf0, 0x22, 0x87, 0xb9, 0xe8, 0x20, 0xdc, 0xee, 0x9f, 0xc2, 0xad, 0x22, };
#ifdef BOOTLOADER_RELEASE
uint8_t fw_public_keys[] = { \
//TODO: generate production keys
};
#else
uint8_t fw_public_keys[] = { \
0xfe, 0xcb, 0x28, 0xb9, 0x50, 0xdd, 0x8b, 0x2f, 0xc7, 0x34, 0xd3, 0x60, 0x5b, 0x1a, 0xc6, 0xed, 0x02, 0x50, 0xf2, 0x4a, 0xc4, 0x75, 0xd1, 0x28, 0x7f, 0x7c, 0xb5, 0xce, 0x61, 0xd6, 0x95, 0xb9, 0xb5, 0x27, 0x0b, 0x52, 0x77, 0x42, 0x4b, 0xf3, 0xb4, 0x3c, 0xef, 0xcb, 0x56, 0xd1, 0x98, 0x22, 0x11, 0xc2, 0xe5, 0xd3, 0xf0, 0x22, 0x87, 0xb9, 0xe8, 0x20, 0xdc, 0xee, 0x9f, 0xc2, 0xad, 0x22, \
0xe8, 0xb4, 0x6f, 0xfd, 0xe2, 0x77, 0xe4, 0xb7, 0x8e, 0x64, 0xed, 0x8d, 0x1d, 0xd4, 0xe0, 0x41, 0x72, 0x40, 0xba, 0xc3, 0xc5, 0x25, 0xbe, 0x53, 0xb7, 0x5e, 0xf9, 0xf5, 0x19, 0xda, 0x03, 0xdf, 0xb9, 0x41, 0xeb, 0x63, 0x39, 0xdf, 0xee, 0x47, 0x9f, 0x86, 0xbf, 0x87, 0x8a, 0xcd, 0xf0, 0x3b, 0x1e, 0x7d, 0x85, 0xd2, 0x3d, 0x96, 0xf2, 0x31, 0x1a, 0x49, 0x0a, 0xd6, 0xad, 0xdd, 0x44, 0xf0, \
0x5c, 0x25, 0xe5, 0x96, 0xa1, 0xc0, 0x17, 0xf2, 0x18, 0x80, 0x3f, 0x99, 0x40, 0xda, 0x02, 0x8a, 0x9a, 0x8c, 0xef, 0x34, 0x60, 0xc9, 0x53, 0x6b, 0x34, 0x07, 0x42, 0x87, 0xce, 0xe7, 0xa1, 0x47, 0x6a, 0x9f, 0xf2, 0x9e, 0xfd, 0xf3, 0xa0, 0x1a, 0xd2, 0x6c, 0xd0, 0x28, 0xc9, 0x4c, 0x21, 0xfb, 0x32, 0xcc, 0x08, 0x56, 0x16, 0xa7, 0x86, 0xcb, 0x36, 0x26, 0x1e, 0x60, 0x58, 0x90, 0x67, 0xae, \
0x37, 0xa5, 0xfb, 0xbc, 0xe9, 0xa9, 0x62, 0x45, 0x0f, 0x71, 0x7a, 0x91, 0x09, 0xb3, 0xe5, 0xfe, 0x2c, 0x37, 0x94, 0x5e, 0xc0, 0x91, 0xe7, 0x91, 0xd8, 0xc0, 0xe3, 0x84, 0x7e, 0x48, 0xe0, 0xe3, 0x5b, 0xb2, 0xf3, 0xe0, 0xc6, 0x86, 0x37, 0xc9, 0xd3, 0x56, 0x7d, 0x5e, 0xe1, 0xfc, 0x71, 0x1f, 0xf0, 0xfb, 0xa0, 0xe5, 0xf4, 0xc8, 0x8f, 0x40, 0x5d, 0x95, 0x0d, 0xd6, 0x51, 0xd3, 0xb3, 0x13, \
};
#endif
int main(void) {
protect_flash();
@ -83,8 +94,8 @@ void protect_flash() {
flash_optunlock();
FLASH_SET_RDP2();
FLASH_WP(FLASH->WRP1AR, BOOTLOADER_FIRST_PAGE, BOOTLOADER_LAST_PAGE);
FLASH_WP(FLASH->WRP1BR, RECOVERY_FW_FIRST_PAGE, RECOVERY_FW_LAST_PAGE);
FLASH_WP(FLASH->WRP1AR, BOOTLOADER_FIRST_PAGE, FIRMWARE_FIRST_PAGE);
FLASH_WP(FLASH->WRP1BR, RECOVERY_FW_FIRST_PAGE, (RECOVERY_FW_FIRST_PAGE + FIRMWARE_PAGE_COUNT));
// This resets the MCU
flash_optprogram();

View File

@ -96,8 +96,8 @@
#include "stm32l4xx.h"
void SystemInit(void) {
/* Run at 16Mhz */
RCC->CR = RCC_CR_MSIRGSEL | RCC_CR_MSIRANGE_8 | RCC_CR_MSION;
/* Run at 16mhz */
RCC->CR = RCC_CR_MSIRGSEL | RCC_CR_MSIRANGE_9 | RCC_CR_MSION;
/* Reset CFGR */
RCC->CFGR = 0x00000000U;
@ -107,6 +107,9 @@ void SystemInit(void) {
/* Disable all interrupts */
RCC->CIER = 0x00000000U;
/* Enter run range 2 */
PWR->CR1 = PWR_CR1_VOS_1;
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@ -19,7 +19,7 @@ def main(index):
print("Firmware must be prepared with fw_prepare.py first!")
exit(1)
sk_pem = open("secret_key.pem", "r").read()
sk_pem = open("secret_key_%d.pem" % index, "r").read()
sk = ecdsa.SigningKey.from_pem(sk_pem)
plain_fw = fw[HEADER_SIZE:]
fw_sig = sk.sign_deterministic(plain_fw, hashfunc=hashlib.sha256)

6
scripts/fw_signall_dev.sh Executable file
View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
./fw_sign.py 0
./fw_sign.py 1
./fw_sign.py 2
./fw_sign.py 3

View File

@ -11,7 +11,7 @@ def main():
vk = sk.get_verifying_key()
vk_hex = binascii.hexlify(vk.to_string())
vk_h = "uint8_t fw_public_key[] = { " + re.sub(r"(..)", "0x\\1, ", vk_hex) + "};"
vk_h = re.sub(r"(..)", "0x\\1, ", vk_hex) + "\\"
open("secret_key.pem","w").write(sk.to_pem())
open("public_key.h","w").write(vk_h)
if __name__ == '__main__':