diff --git a/README.md b/README.md index 27a8db953d..d7e0f41dfa 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Design based on [RFC 9019](https://datatracker.ietf.org/doc/rfc9019/) - A Firmwa This repository contains the following components: - the wolfBoot bootloader - - key generator and image signing tools (requires python 3.x and wolfcrypt-py https://github.com/wolfSSL/wolfcrypt-py) + - key generator and image signing tools - Baremetal test applications ### wolfBoot bootloader @@ -63,17 +63,17 @@ Additional examples available on our GitHub wolfBoot-examples repository [here]( The following steps are automated in the default `Makefile` target, using the baremetal test application as an example to create the factory image. By running `make`, the build system will: - - Create a Ed25519 Key-pair using the `ed25519_keygen` tool + - Create a Ed25519 Key-pair using the `keygen` tool - Compile the bootloader. The public key generated in the step above is included in the build - Compile the firmware image from the test application in [test\_app](test-app/) - Re-link the firmware to change the entry-point to the start address of the primary partition - - Sign the firmware image using the `ed25519_sign` tool + - Sign the firmware image using the `sign` tool - Create a factory image by concatenating the bootloader and the firmware image The factory image can be flashed to the target device. It contains the bootloader and the signed initial firmware at the specified address on the flash. -The `sign.py` tool transforms a bootable firmware image to comply with the firmware image format required by the bootloader. +The `sign` tool transforms a bootable firmware image to comply with the firmware image format required by the bootloader. For detailed information about the firmware image format, see [Firmware image](docs/firmware_image.md) @@ -82,7 +82,7 @@ For detailed information about the configuration options for the target system, ### Upgrading the firmware - Compile the new firmware image, and link it so that its entry point is at the start address of the primary partition - - Sign the firmware using the `sign.py` tool and the private key generated for the factory image + - Sign the firmware using the `sign` tool and the private key generated for the factory image - Transfer the image using a secure connection, and store it to the secondary firmware slot - Trigger the image swap using libwolfboot `wolfBoot_update_trigger()` function. See [wolfBoot library API](docs/API.md) for a description of the operation - Reboot to let the bootloader begin the image swap @@ -171,45 +171,13 @@ guidance and worked SBOM examples, see the ## Troubleshooting -1. Python errors when signing a key: - -``` -Traceback (most recent call last): - File "tools/keytools/keygen.py", line 135, in - rsa = ciphers.RsaPrivate.make_key(2048) -AttributeError: type object 'RsaPrivate' has no attribute 'make_key' -``` - -``` -Traceback (most recent call last): - File "tools/keytools/sign.py", line 189, in - r, s = ecc.sign_raw(digest) -AttributeError: 'EccPrivate' object has no attribute 'sign_raw' -``` - -You need to install the latest wolfcrypt-py here: https://github.com/wolfSSL/wolfcrypt-py - -Use `pip3 install wolfcrypt`. - -Or to install based on a local wolfSSL installation use: - -```sh -cd wolfssl -./configure --enable-keygen --enable-rsa --enable-ecc --enable-ed25519 --enable-des3 CFLAGS="-DFP_MAX_BITS=8192 -DWOLFSSL_PUBLIC_MP" -make -sudo make install - -cd wolfcrypt-py -USE_LOCAL_WOLFSSL=/usr/local pip3 install . -``` - -2. Key algorithm mismatch: +1. Key algorithm mismatch: The error `Key algorithm mismatch. Remove old keys via 'make keysclean'` indicates the current `.config` `SIGN` algorithm does not match what is in the generated `src/keystore.c` file. Use `make keysclean` to delete keys and regenerate. -3. Cannot open compiler generated file ... Permission denied +2. Cannot open compiler generated file ... Permission denied This may occur due to multiple environments being opened concurrently, or anti-virus software. Try manually deleting the respective build directories and/or restarting your IDE. diff --git a/hal/max32666.c b/hal/max32666.c index a8fbaf117c..c822497b3c 100644 --- a/hal/max32666.c +++ b/hal/max32666.c @@ -421,6 +421,11 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) { int ret; volatile uint32_t *flc_base; + uint32_t end; + + /* Drive the loop from the end of the requested range so the tail is + * erased when the start address is rounded back to a page. */ + end = address + (uint32_t)len; /* Align to page boundary */ if (address & (FLASH_PAGE_SIZE - 1)) { @@ -429,7 +434,7 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) icc_disable(); - while (len > 0) { + while (address < end) { flc_base = flc_base_for_addr(address); ret = flc_page_erase(address, flc_base); @@ -439,7 +444,6 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) } address += FLASH_PAGE_SIZE; - len -= FLASH_PAGE_SIZE; } icc_enable(); diff --git a/hal/mcxn.c b/hal/mcxn.c index da96b747d6..6b30362926 100644 --- a/hal/mcxn.c +++ b/hal/mcxn.c @@ -310,16 +310,20 @@ void RAMFUNCTION hal_flash_lock(void) int RAMFUNCTION hal_flash_erase(uint32_t address, int len) { uint32_t sector_size = pflash_sector_size; + uint32_t end; if (sector_size == 0U) { sector_size = WOLFBOOT_SECTOR_SIZE; } + /* Drive the loop from the end of the requested range so the tail is + * erased when the start address is rounded back to a sector. */ + end = address + (uint32_t)len; if ((address % sector_size) != 0U) { address -= address % sector_size; } - while (len > 0) { + while (address < end) { if (FLASH_Erase(&pflash, address, sector_size, kFLASH_ApiEraseKey) != kStatus_FLASH_Success) { return -1; @@ -329,7 +333,6 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) return -1; } address += sector_size; - len -= (int)sector_size; } return 0; diff --git a/hal/sim.c b/hal/sim.c index 6393e07e25..ee0395efba 100644 --- a/hal/sim.c +++ b/hal/sim.c @@ -539,6 +539,10 @@ void hal_init(void) for (i = 1; i < main_argc; i++) { if (strcmp(main_argv[i], "powerfail") == 0) { + if ((i + 1) >= main_argc) { + wolfBoot_printf( "powerfail requires a hex address argument\n"); + exit(-1); + } erasefail_address = strtol(main_argv[++i], NULL, 16); wolfBoot_printf( "Set power fail to erase at address %x\n", erasefail_address); diff --git a/hal/spi/spi_drv_nrf54l.c b/hal/spi/spi_drv_nrf54l.c index 4bf58583ba..2a09d0897c 100644 --- a/hal/spi/spi_drv_nrf54l.c +++ b/hal/spi/spi_drv_nrf54l.c @@ -96,8 +96,14 @@ void RAMFUNCTION spi_write(const char byte) ; SPI_EVENTS_STOPPED = 0; - if (SPI_EVENTS_DMA_RX_BUSERROR == 0 && SPI_EVENTS_DMA_TX_BUSERROR == 0) + if (SPI_EVENTS_DMA_RX_BUSERROR == 0 && SPI_EVENTS_DMA_TX_BUSERROR == 0) { spi_rx_ready = 1; + } else { + /* DMA bus error: force a defined byte and unblock the caller, or + * spi_read() would spin forever on spi_rx_ready == 0. */ + spi_rx_byte = 0xFF; + spi_rx_ready = 1; + } } diff --git a/hal/stm32g0.c b/hal/stm32g0.c index 8becc90f97..aea0fc8bc4 100644 --- a/hal/stm32g0.c +++ b/hal/stm32g0.c @@ -150,18 +150,17 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) flash_wait_complete(); i+=8; } else { + uint32_t unit_addr = (address + i) & (~0x07); + int off = (address + i) - unit_addr; uint32_t val[2]; uint8_t *vbytes = (uint8_t *)(val); - int off = (address + i) - (((address + i) >> 3) << 3); - uint32_t base_addr = address & (~0x07); /* aligned to 64 bit */ - int u32_idx = (i >> 2); - dst = (uint32_t *)(base_addr); - val[0] = dst[u32_idx]; - val[1] = dst[u32_idx + 1]; + dst = (uint32_t *)unit_addr; + val[0] = dst[0]; + val[1] = dst[1]; while ((off < 8) && (i < len)) vbytes[off++] = data[i++]; - dst[u32_idx] = val[0]; - dst[u32_idx + 1] = val[1]; + dst[0] = val[0]; + dst[1] = val[1]; flash_wait_complete(); } } diff --git a/hal/stm32l4.c b/hal/stm32l4.c index 84bcc85842..10bc6f2917 100644 --- a/hal/stm32l4.c +++ b/hal/stm32l4.c @@ -146,18 +146,17 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) flash_wait_complete(); i+=8; } else { + uint32_t unit_addr = (address + i) & (~0x07); + int off = (address + i) - unit_addr; uint32_t val[2]; uint8_t *vbytes = (uint8_t *)(val); - int off = (address + i) - (((address + i) >> 3) << 3); - uint32_t base_addr = address & (~0x07); /* aligned to 64 bit */ - int u32_idx = (i >> 2); - dst = (uint32_t *)(base_addr); - val[0] = dst[u32_idx]; - val[1] = dst[u32_idx + 1]; + dst = (uint32_t *)unit_addr; + val[0] = dst[0]; + val[1] = dst[1]; while ((off < 8) && (i < len)) vbytes[off++] = data[i++]; - dst[u32_idx] = val[0]; - dst[u32_idx + 1] = val[1]; + dst[0] = val[0]; + dst[1] = val[1]; flash_wait_complete(); } } diff --git a/hal/stm32wb.c b/hal/stm32wb.c index 805f900482..2cccf81434 100644 --- a/hal/stm32wb.c +++ b/hal/stm32wb.c @@ -204,18 +204,17 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) flash_wait_complete(); i+=8; } else { + uint32_t unit_addr = (address + i) & (~0x07); + int off = (address + i) - unit_addr; uint32_t val[2]; uint8_t *vbytes = (uint8_t *)(val); - int off = (address + i) - (((address + i) >> 3) << 3); - uint32_t base_addr = address & (~0x07); /* aligned to 64 bit */ - int u32_idx = (i >> 2); - dst = (uint32_t *)(base_addr); - val[0] = dst[u32_idx]; - val[1] = dst[u32_idx + 1]; + dst = (uint32_t *)unit_addr; + val[0] = dst[0]; + val[1] = dst[1]; while ((off < 8) && (i < len)) vbytes[off++] = data[i++]; - dst[u32_idx] = val[0]; - dst[u32_idx + 1] = val[1]; + dst[0] = val[0]; + dst[1] = val[1]; flash_wait_complete(); } } diff --git a/include/delta.h b/include/delta.h index 9066cc8d5a..1625cbe566 100644 --- a/include/delta.h +++ b/include/delta.h @@ -9,7 +9,7 @@ * * Compile with DELTA_UPDATES=1 * - * Use tools/sign.py or tool/sign.c on the host to provide small + * Use the sign tool (tools/keytools/sign.c) on the host to provide small * secure update packages containing only binary difference, using the * --delta option. * diff --git a/src/disk.c b/src/disk.c index 79114a6ca6..b0b9a21a01 100644 --- a/src/disk.c +++ b/src/disk.c @@ -104,6 +104,10 @@ static int disk_open_mbr(struct disk_drive *drive, const uint8_t *mbr_sector) } } + if (drive->n_parts == 0) { + return -1; /* no usable partition entries */ + } + return drive->n_parts; } diff --git a/src/image.c b/src/image.c index dc48fc860c..ee23840c33 100644 --- a/src/image.c +++ b/src/image.c @@ -417,6 +417,9 @@ static void wolfBoot_verify_signature_ecc(uint8_t key_slot, mp_read_unsigned_bin(&s, sig + point_sz, point_sz); VERIFY_FN(img, &verify_res, wc_ecc_verify_hash_ex, &r, &s, img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, &verify_res, &ecc); + /* Signature scalars: scrub before the stack frame retires. */ + mp_clear(&r); + mp_clear(&s); } #endif } diff --git a/src/qspi_flash.c b/src/qspi_flash.c index 64638904ff..5ca906d559 100644 --- a/src/qspi_flash.c +++ b/src/qspi_flash.c @@ -533,7 +533,7 @@ static int test_ext_flash(void) #endif if (pageData[i] != (i & 0xff)) { wolfBoot_printf("Check Data @ %d failed\n", i); - return -i; + return -1; } } diff --git a/src/sdhci.c b/src/sdhci.c index 51a9c179a9..c078882da3 100644 --- a/src/sdhci.c +++ b/src/sdhci.c @@ -1187,7 +1187,10 @@ static int sdcard_card_full_init(void) } if (status == 0) { - sdhci_set_clock(SDHCI_CLK_50MHZ); + if (sdhci_set_clock(SDHCI_CLK_50MHZ) == 0) { + wolfBoot_printf("UHS-I: failed to set 50MHz clock\n"); + status = -1; + } } SDHCI_REG_SET(SDHCI_SRS13, irq_restore); /* re-enable interrupt */ @@ -1235,7 +1238,7 @@ static int sdcard_send_switch_function(uint32_t mode, uint32_t function_number, uint32_t func_status[64/sizeof(uint32_t)]; /* fixed 512 bits */ uint8_t* p_func_status = (uint8_t*)func_status; - if (group_number > 6 || function_number > 15) { + if (group_number < 1 || group_number > 6 || function_number > 15) { return -1; /* Invalid group or function number */ } @@ -1268,6 +1271,11 @@ static int sdcard_send_switch_function(uint32_t mode, uint32_t function_number, break; } } while (status == 0 && --timeout > 0); /* retry until function not busy */ + + if (timeout == 0) { + /* Card stayed busy until the retry budget ran out. */ + status = -1; + } return status; } @@ -1495,7 +1503,10 @@ static int emmc_card_full_init(void) } /* Set clock to 25MHz for legacy mode */ - sdhci_set_clock(SDHCI_CLK_25MHZ); + if (sdhci_set_clock(SDHCI_CLK_25MHZ) == 0) { + wolfBoot_printf("eMMC: failed to set 25MHz clock\n"); + return -1; + } /* Enable high speed if desired (optional for legacy mode) */ sdhci_reg_or(SDHCI_SRS10, SDHCI_SRS10_HSE); @@ -2000,7 +2011,10 @@ int sdhci_init(void) SDHCI_REG_SET(SDHCI_SRS10, reg); /* Setup 400khz starting clock */ - sdhci_set_clock(SDHCI_CLK_400KHZ); + if (sdhci_set_clock(SDHCI_CLK_400KHZ) == 0) { + wolfBoot_printf("Failed to set 400kHz starting clock\n"); + return -1; + } /* Allow clock to stabilize before issuing first command */ udelay(1000); /* 1ms */ diff --git a/src/tpm.c b/src/tpm.c index e240946e86..5679cdd75c 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -720,6 +720,8 @@ int wolfBoot_store_blob(TPMI_RH_NV_AUTH authHandle, uint32_t nvIndex, wolfBoot_printf("Error %d writing blob to NV index %x (error %s)\n", rc, nv.handle.hndl, wolfTPM2_GetRCString(rc)); } + /* Scrub the stack NV handle: it carries the authValue copy. */ + TPM2_ForceZero(&nv, sizeof(nv)); return rc; } @@ -792,6 +794,7 @@ int wolfBoot_read_blob(uint32_t nvIndex, WOLFTPM2_KEYBLOB* blob, wolfBoot_printf("Error %d reading blob from NV index %x (error %s)\n", rc, nv.handle.hndl, wolfTPM2_GetRCString(rc)); } + TPM2_ForceZero(&nv, sizeof(nv)); return rc; } @@ -825,6 +828,7 @@ int wolfBoot_delete_blob(TPMI_RH_NV_AUTH authHandle, uint32_t nvIndex, wolfBoot_printf("Error %d deleting blob from NV index %x (error %s)\n", rc, nv.handle.hndl, wolfTPM2_GetRCString(rc)); } + TPM2_ForceZero(&nv, sizeof(nv)); return rc; } @@ -908,6 +912,8 @@ int wolfBoot_seal_blob(const uint8_t* pubkey_hint, wolfTPM2_UnloadHandle(&wolftpm_dev, &policy_session.handle); wolfTPM2_UnsetAuthSession(&wolftpm_dev, 1, &wolftpm_session); + /* Scrub the session object: it holds the SRK-derived session key. */ + TPM2_ForceZero(&policy_session, sizeof(policy_session)); return rc; } @@ -973,6 +979,8 @@ int wolfBoot_seal_auth(const uint8_t* pubkey_hint, wolfBoot_printf("Error %d sealing secret! (%s)\n", rc, wolfTPM2_GetRCString(rc)); } + /* The blob holds the plaintext authValue copy used for the seal. */ + TPM2_ForceZero(&seal_blob, sizeof(seal_blob)); return rc; } int wolfBoot_seal(const uint8_t* pubkey_hint, @@ -1169,6 +1177,7 @@ int wolfBoot_unseal_blob(const uint8_t* pubkey_hint, wolfTPM2_UnloadHandle(&wolftpm_dev, &seal_blob->handle); wolfTPM2_UnloadHandle(&wolftpm_dev, &policy_session.handle); wolfTPM2_UnsetAuthSession(&wolftpm_dev, 1, &wolftpm_session); + TPM2_ForceZero(&policy_session, sizeof(policy_session)); return rc; } @@ -1202,6 +1211,7 @@ int wolfBoot_unseal_auth(const uint8_t* pubkey_hint, wolfBoot_printf("Error %d unsealing secret! (%s)\n", rc, wolfTPM2_GetRCString(rc)); } + TPM2_ForceZero(&seal_blob, sizeof(seal_blob)); return rc; } int wolfBoot_unseal(const uint8_t* pubkey_hint, @@ -1652,6 +1662,14 @@ void wolfBoot_tpm2_deinit(void) #endif /* WOLFBOOT_TPM_KEYSTORE */ wolfTPM2_Cleanup(&wolftpm_dev); + +#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL) + /* The OS takes over from here: leave no session key or SRK auth in + * SRAM. UnloadHandle flushes the TPM-side context but is not + * documented to clear handle->auth. */ + TPM2_ForceZero(&wolftpm_session, sizeof(wolftpm_session)); + TPM2_ForceZero(&wolftpm_srk, sizeof(wolftpm_srk)); +#endif } /** @@ -1721,6 +1739,7 @@ int wolfBoot_check_rot(int key_slot, uint8_t* pubkey_hint) } wolfTPM2_UnsetAuthSession(&wolftpm_dev, 1, &wolftpm_session); + TPM2_ForceZero(&nv, sizeof(nv)); return rc; } #endif diff --git a/src/uart_flash.c b/src/uart_flash.c index 3623c9c9eb..7e7b3b98f8 100644 --- a/src/uart_flash.c +++ b/src/uart_flash.c @@ -47,17 +47,22 @@ int uart_tx(const uint8_t c); int uart_rx(uint8_t *c); -static int wait_ack(void) +static int wait_ack_cycles(int cycles) { + uint8_t c; volatile int count = 0; - while(++count < WAIT_CYCLES) { - uint8_t c; + while(++count < cycles) { if ((uart_rx(&c) == 1) && (c == CMD_ACK)) return 0; } return -1; } +static int wait_ack(void) +{ + return wait_ack_cycles(WAIT_CYCLES); +} + static int uart_rx_timeout(uint8_t *c) { volatile int count = 0; @@ -144,7 +149,7 @@ int ext_flash_erase(uintptr_t address, int len) return -1; } /* Wait for extra ack at the end of Erase */ - if (wait_ack() == 0) + if (wait_ack_cycles(WAIT_CYCLES * ERASE_TIMEOUT) == 0) return 0; return -1; } diff --git a/src/update_disk.c b/src/update_disk.c index 8a78a64c79..e74bf986d4 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -226,6 +226,9 @@ static void disk_crypto_set_iv(uint32_t block_offset) iv[15] = (uint8_t)(ctr); wc_AesSetIV(&aes_dec, iv); + /* Scrub the stack copy: the counter bytes are derived from the + * secret disk-encryption nonce (matches aes_set_iv in libwolfboot.c). */ + wc_ForceZero(iv, sizeof(iv)); #endif } diff --git a/src/xmalloc.c b/src/xmalloc.c index a270c2fffc..46930e8b7f 100644 --- a/src/xmalloc.c +++ b/src/xmalloc.c @@ -26,6 +26,7 @@ #include #include #include +#include /* wc_ForceZero */ #ifndef USE_FAST_MATH #include #include @@ -514,6 +515,9 @@ void XFREE(void *ptr, void *heap, int type) #endif while (xmalloc_pool[i].addr) { if ((ptr == (void *)(xmalloc_pool[i].addr)) && xmalloc_pool[i].in_use) { + /* Scrub the slot before releasing it: it may hold crypto + * workspace (hash blocks, signature verification state). */ + wc_ForceZero(xmalloc_pool[i].addr, xmalloc_pool[i].size); xmalloc_pool[i].in_use = 0; return; } diff --git a/tools/keytools/keygen.py b/tools/keytools/keygen.py deleted file mode 100644 index fc250d77c3..0000000000 --- a/tools/keytools/keygen.py +++ /dev/null @@ -1,399 +0,0 @@ -#!/usr/bin/python3 -''' - * keygen.py - * - * Copyright (C) 2026 wolfSSL Inc. - * - * This file is part of wolfBoot. - * - * wolfBoot is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * wolfBoot is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA -''' - -import sys,os,struct -from wolfcrypt import ciphers - -AUTH_KEY_ED25519 = 0x01 -AUTH_KEY_ECC256 = 0x02 -AUTH_KEY_RSA2048 = 0x03 -AUTH_KEY_RSA4096 = 0x04 -AUTH_KEY_ED448 = 0x05 -AUTH_KEY_ECC384 = 0x06 -AUTH_KEY_ECC521 = 0x07 -AUTH_KEY_RSA3072 = 0x08 - -#default sign algorithm value -sign="ed25519" - - -def usage(): - print("Usage: %s [--ed25519 | --ed448 | --ecc256 | --ecc384 | --ecc521 | --rsa2048| --rsa3072 | --rsa4096] [ --force ] [-i pubkey0.der [-i pubkey1.der -i pubkey2.der ... -i pubkeyN.der]] [-i pubkey0.der [-i pubkey1.der -i pubkey2.der ... -i pubkeyN.der] [-keystoreDir dir]]n" % sys.argv[0]) - parser.print_help() - sys.exit(1) - -def dupsign(): - print("") - print("Error: only one algorithm must be specified.") - print("") - usage() - -def sign_key_type(name): - if name == 'ed25519': - return 'AUTH_KEY_ED25519' - elif name == 'ed448': - return 'AUTH_KEY_ED448' - elif name == 'ecc256': - return 'AUTH_KEY_ECC256' - elif name == 'ecc384': - return 'AUTH_KEY_ECC384' - elif name == 'ecc521': - return 'AUTH_KEY_ECC521' - elif name == 'rsa2048': - return 'AUTH_KEY_RSA2048' - elif name == 'rsa3072': - return 'AUTH_KEY_RSA3072' - elif name == 'rsa4096': - return 'AUTH_KEY_RSA4096' - else: - return 0 - -def sign_key_size(name): - if name == 'ed25519': - return 'KEYSTORE_PUBKEY_SIZE_ED25519' - elif name == 'ed448': - return 'KEYSTORE_PUBKEY_SIZE_ED448' - elif name == 'ecc256': - return 'KEYSTORE_PUBKEY_SIZE_ECC256' - elif name == 'ecc384': - return 'KEYSTORE_PUBKEY_SIZE_ECC384' - elif name == 'ecc521': - return 'KEYSTORE_PUBKEY_SIZE_ECC521' - elif name == 'rsa2048': - return 'KEYSTORE_PUBKEY_SIZE_RSA2048' - elif name == 'rsa3072': - return 'KEYSTORE_PUBKEY_SIZE_RSA3072' - elif name == 'rsa4096': - return 'KEYSTORE_PUBKEY_SIZE_RSA4096' - else: - return 0 - -def sign_key_size_literal(name): - if name == 'ed25519': - return 32 - elif name == 'ed448': - return 57 - elif name == 'ecc256': - return 64 - elif name == 'ecc384': - return 96 - elif name == 'ecc521': - return 132 - elif name == 'rsa2048': - return 320 - elif name == 'rsa3072': - return 448 - elif name == 'rsa4096': - return 576 - else: - return 0 - -def keystore_add(slot, pub, sz = 0): - ktype = sign_key_type(sign) - if (sz == 0): - ksize = sign_key_size(sign) - else: - ksize = str(sz) - pfile.write(Slot_hdr % (key_file, slot, ktype, ksize)) - i = 0 - for c in bytes(pub[0:-1]): - pfile.write("0x%02X, " % c) - i += 1 - if (i % 8 == 0): - pfile.write('\n\t\t\t') - pfile.write("0x%02X" % pub[-1]) - pfile.write(Pubkey_footer) - pfile.write(Slot_footer) - t = 0x8A8A8A8A - m = 0xFFFFFFFF - ks_struct = struct.pack("\n#include \"wolfboot/wolfboot.h\"\n#include \"keystore.h\"\n" \ - "#ifdef WOLFBOOT_NO_SIGN\n\t#define NUM_PUBKEYS 0\n#else\n\n" \ - "#if !defined(KEYSTORE_ANY) && (KEYSTORE_PUBKEY_SIZE != KEYSTORE_PUBKEY_SIZE_%s)\n\t" \ - "#error Key algorithm mismatch. Remove old keys via 'make keysclean'\n" \ - "#else\n" - - -Store_hdr = "#define NUM_PUBKEYS %d\nconst struct keystore_slot PubKeys[NUM_PUBKEYS] = {\n\n" -Slot_hdr = "\t /* Key associated to file '%s' */\n" -Slot_hdr += "\t{\n\t\t.slot_id = %d,\n\t\t.key_type = %s,\n" -Slot_hdr += "\t\t.part_id_mask = KEY_VERIFY_ALL,\n\t\t.pubkey_size = %s,\n" -Slot_hdr += "\t\t.pubkey = {\n\t\t\t" -Pubkey_footer = "\n\t\t}," -Slot_footer = "\n\t},\n\n" -Store_footer = '\n};\n\n' - -Keystore_API = "int keystore_num_pubkeys(void)\n" -Keystore_API += "{\n" -Keystore_API += " return NUM_PUBKEYS;\n" -Keystore_API += "}\n\n" -Keystore_API += "uint8_t *keystore_get_buffer(int id)\n" -Keystore_API += "{\n" -Keystore_API += " if (id >= keystore_num_pubkeys())\n" -Keystore_API += " return (uint8_t *)0;\n" -Keystore_API += " return (uint8_t *)PubKeys[id].pubkey;\n" -Keystore_API += "}\n\n" -Keystore_API += "int keystore_get_size(int id)\n" -Keystore_API += "{\n" -Keystore_API += " if (id >= keystore_num_pubkeys())\n" -Keystore_API += " return -1;\n" -Keystore_API += " return (int)PubKeys[id].pubkey_size;\n" -Keystore_API += "}\n\n" -Keystore_API += "uint32_t keystore_get_mask(int id)\n" -Keystore_API += "{\n" -Keystore_API += " if (id >= keystore_num_pubkeys())\n" -Keystore_API += " return -1;\n" -Keystore_API += " return PubKeys[id].part_id_mask;\n" -Keystore_API += "}\n\n" -Keystore_API += "#endif /* Keystore public key size check */\n" -Keystore_API += "#endif /* WOLFBOOT_NO_SIGN */\n" - - -import argparse as ap - -parser = ap.ArgumentParser(prog='keygen.py', description='wolfBoot key generation tool') -parser.add_argument('--ed25519', dest='ed25519', action='store_true') -parser.add_argument('--ed448', dest='ed448', action='store_true') -parser.add_argument('--ecc256', dest='ecc256', action='store_true') -parser.add_argument('--ecc384', dest='ecc384', action='store_true') -parser.add_argument('--ecc521', dest='ecc521', action='store_true') -parser.add_argument('--rsa2048', dest='rsa2048', action='store_true') -parser.add_argument('--rsa3072', dest='rsa3072', action='store_true') -parser.add_argument('--rsa4096', dest='rsa4096', action='store_true') -parser.add_argument('--force', dest='force', action='store_true') -parser.add_argument('-i', dest='pubfile', nargs='+', action='extend') -parser.add_argument('-g', dest='keyfile', nargs='+', action='extend') -parser.add_argument('-keystoreDir', dest='storeDir', nargs='+', action='extend') - -print(" *** WARNING ***") -print("Python key tools are now deprecated") -print("and will be removed in future versions.") -print("Please ensure that your scripts are using") -print("the compiled C version of these tools") -print("(e.g. by running 'make keytools').") -print(" *** ******* ***") -print("") - -args=parser.parse_args() - -if (type(args.storeDir) == list): - pubkey_cfile = "".join(args.storeDir)+"/keystore.c" - keystore_imgfile = "".join(args.storeDir)+"/keystore.der" -else: - pubkey_cfile = "src/keystore.c" - keystore_imgfile = "keystore.der" - -key_files = args.keyfile -pubkey_files = args.pubfile - -if pubkey_files == None: - pubkey_files = [] - -if key_files == None: - key_files = [] - -print("keys to import:") -print(pubkey_files) -print("keys to generate:") -print(key_files) - - -sign=None -force=False -if (args.ed25519): - sign='ed25519' -if (args.ed448): - if sign is not None: - dupsign() - sign='ed448' -if (args.ecc256): - if sign is not None: - dupsign() - sign='ecc256' -if (args.ecc384): - if sign is not None: - dupsign() - sign='ecc384' -if (args.ecc521): - if sign is not None: - dupsign() - sign='ecc521' - print("ecc521 keys are not yet supported!") - sys.exit(1) -if (args.rsa2048): - if sign is not None: - dupsign() - sign='rsa2048' -if (args.rsa3072): - if sign is not None: - dupsign() - sign='rsa3072' -if (args.rsa4096): - if sign is not None: - dupsign() - sign='rsa4096' - -if sign is None: - usage() - -force = args.force - - -if pubkey_cfile[-2:] != '.c': - print("** Warning: generated public key cfile does not have a '.c' extension") - -# Create/open public key c file -print ("Output C file: " + pubkey_cfile) -pfile = open(pubkey_cfile, "w") -pfile.write(Cfile_Banner % sign.upper()) -pfile.write(Store_hdr % (len(key_files) + len(pubkey_files))) -ksfile = open(keystore_imgfile, "wb") - -pub_slot_index = 0 - - -if pubkey_files != None: - for pub_slot_index, key_file in enumerate(pubkey_files): - print ("Public key slot: " + str(pub_slot_index)) - print ("Selected cipher: " + sign) - print ("Input public key: " + key_file) - with open(key_file, 'rb') as f: - key = f.read(4096) - # if it's an ecc key and it's length is longer than the raw key we - # need to parse it - if (sign == 'ecc256' or sign == 'ecc384' or sign == 'ecc521') and len(key) > sign_key_size_literal(sign): - eccKey = ciphers.EccPublic(key) - key = eccKey.encode_key_raw() - key = key[0] + key[1] - keystore_add(pub_slot_index, key) - pub_slot_index = len(pubkey_files) - -for slot_index_off, key_file in enumerate(key_files): - slot_index = slot_index_off + pub_slot_index - print ("Public key slot: " + str(slot_index)) - print ("Selected cipher: " + sign) - print ("Output Private key: " + key_file) - print() - if os.path.exists(key_file) and not force: - choice = input("** Warning: key file already exist! Are you sure you want to "+ - "generate a new key and overwrite the existing key? [Type 'Yes']: ") - if (choice != "Yes"): - print("Operation canceled.") - sys.exit(2) - - if (sign == "ed25519"): - ed = ciphers.Ed25519Private.make_key(32) - priv,pub = ed.encode_key() - - print() - print("Creating file " + key_file) - with open(key_file, "wb") as f: - f.write(priv) - f.write(pub) - f.close() - keystore_add(slot_index, pub) - - if (sign == "ed448"): - ed = ciphers.Ed448Private.make_key(57) - priv,pub = ed.encode_key() - print() - print("Creating file " + key_file) - with open(key_file, "wb") as f: - f.write(priv) - f.write(pub) - f.close() - keystore_add(slot_index, pub) - - if (sign[0:3] == 'ecc'): - if (sign == "ecc256"): - ec = ciphers.EccPrivate.make_key(32) - ecc_pub_key_len = 64 - qx,qy,d = ec.encode_key_raw() - - if (sign == "ecc384"): - ec = ciphers.EccPrivate.make_key(48) - ecc_pub_key_len = 96 - qx,qy,d = ec.encode_key_raw() - - if (sign == "ecc521"): - ec = ciphers.EccPrivate.make_key(66) - ecc_pub_key_len = 132 - qx,qy,d = ec.encode_key_raw() - print() - print("Creating file " + key_file) - keystore_add(slot_index, bytes(qx) + bytes(qy)) - with open(key_file, "wb") as f: - f.write(qx) - f.write(qy) - f.write(d) - f.close() - - if (sign == "rsa2048"): - rsa = ciphers.RsaPrivate.make_key(2048) - priv,pub = rsa.encode_key() - print() - print("Creating file " + key_file) - with open(key_file, "wb") as f: - f.write(priv) - f.close() - print("Creating file " + pubkey_cfile) - keystore_add(slot_index, pub, len(pub)) - - if (sign == "rsa3072"): - rsa = ciphers.RsaPrivate.make_key(3072) - priv,pub = rsa.encode_key() - print() - print("Creating file " + key_file) - with open(key_file, "wb") as f: - f.write(priv) - f.close() - keystore_add(slot_index, pub, len(pub)) - - if (sign == "rsa4096"): - rsa = ciphers.RsaPrivate.make_key(4096) - if os.path.exists(key_file) and not force: - choice = input("** Warning: key file already exist! Are you sure you want to "+ - "generate a new key and overwrite the existing key? [Type 'Yes']: ") - if (choice != "Yes"): - print("Operation canceled.") - sys.exit(2) - priv,pub = rsa.encode_key() - print() - print("Creating file " + key_file) - with open(key_file, "wb") as f: - f.write(priv) - f.close() - keystore_add(slot_index, pub, len(pub)) - -pfile.write(Store_footer) -pfile.write(Keystore_API) -pfile.close() diff --git a/tools/keytools/sign.c b/tools/keytools/sign.c index 221f142e92..7f6e804927 100644 --- a/tools/keytools/sign.c +++ b/tools/keytools/sign.c @@ -1968,7 +1968,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, if (read_sz > 32) read_sz = 32; io_sz = (int)fread(buf, 1, read_sz, f); - if ((io_sz < 0) && !feof(f)) { + if (io_sz != (int)read_sz) { ret = -1; break; } @@ -2045,7 +2045,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, if (read_sz > 32) read_sz = 32; io_sz = (int)fread(buf, 1, read_sz, f); - if ((io_sz < 0) && !feof(f)) { + if (io_sz != (int)read_sz) { ret = -1; break; } @@ -2120,7 +2120,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, if (read_sz > 128) read_sz = 128; io_sz = (int)fread(buf, 1, read_sz, f); - if ((io_sz < 0) && !feof(f)) { + if (io_sz != (int)read_sz) { ret = -1; break; } @@ -2973,6 +2973,7 @@ uint64_t arg2num(const char *arg, size_t len) break; case 4: ret &= 0xFFFFFFFF; + break; case 8: break; default: @@ -3357,6 +3358,8 @@ int main(int argc, char** argv) { int ret = 0; int i; + int pos_args; + int need; char* tmpstr; const char* sign_str = "AUTO"; const char* hash_str = "SHA256"; @@ -3581,6 +3584,10 @@ int main(int argc, char** argv) CMD.header_only = 1; } else if (strcmp(argv[i], "--id") == 0) { + if (argc <= (i + 1)) { + fprintf(stderr, "Missing --id argument\n"); + exit(16); + } long id = strtol(argv[++i], NULL, 10); if ((id < 0 || id > 15) || ((id == 0) && (argv[i][0] != '0'))) { fprintf(stderr, "Invalid partition id: %s\n", argv[i]); @@ -3597,6 +3604,10 @@ int main(int argc, char** argv) CMD.manual_sign = 1; } else if (strcmp(argv[i], "--encrypt") == 0) { + if (argc <= (i + 1)) { + fprintf(stderr, "Missing --encrypt key file argument\n"); + exit(16); + } if (CMD.encrypt == ENC_OFF) CMD.encrypt = ENC_CHACHA; CMD.encrypt_key_file = argv[++i]; @@ -3611,6 +3622,10 @@ int main(int argc, char** argv) CMD.encrypt = ENC_CHACHA; } else if (strcmp(argv[i], "--delta") == 0) { + if (argc <= (i + 1)) { + fprintf(stderr, "Missing --delta base file argument\n"); + exit(16); + } CMD.delta = 1; CMD.delta_base_file = argv[++i]; } else if (strcmp(argv[i], "--no-base-sha") == 0) { @@ -3620,6 +3635,10 @@ int main(int argc, char** argv) CMD.no_ts = 1; } else if (strcmp(argv[i], "--policy") == 0) { + if (argc <= (i + 1)) { + fprintf(stderr, "Missing --policy file argument\n"); + exit(16); + } CMD.policy_sign = 1; CMD.policy_file = argv[++i]; } @@ -3896,6 +3915,24 @@ int main(int argc, char** argv) CMD.secondary_signature_sz = 0; } + /* Validate the positional argument count for the selected mode: image + + * version, plus key (and secondary key when hybrid) when signing, plus + * the precomputed signature file with --manual-sign. */ + pos_args = argc - (i + 1); + need = 2; /* image file + version */ + if (CMD.sign != NO_SIGN) { + need += 1; /* key file */ + if (CMD.hybrid) + need += 1; /* secondary key file */ + if (CMD.manual_sign) + need += 1; /* precomputed signature file */ + } + if (pos_args < need) { + fprintf(stderr, "Missing positional arguments: need %d, got %d " + "(image key version)\n", need, pos_args); + exit(1); + } + if (CMD.sign != NO_SIGN) { if (CMD.hybrid) { @@ -3976,7 +4013,7 @@ int main(int argc, char** argv) } if (CMD.delta) { printf("Delta Base file: %s\n", CMD.delta_base_file); - snprintf(CMD.output_diff_file, sizeof(CMD.output_image_file), + snprintf(CMD.output_diff_file, sizeof(CMD.output_diff_file), "%s_v%s_signed_diff.bin", (char*)buf, CMD.fw_version); snprintf(CMD.output_encrypted_image_file, @@ -4097,5 +4134,11 @@ int main(int argc, char** argv) if (CMD.hybrid) { free_key(CMD.secondary_sign, 1); } + /* Defence in depth: scrub the decoded key objects regardless of the + * algorithm dispatch above, so no key residue survives. */ + wc_ForceZero(&key, sizeof(key)); + if (CMD.hybrid) { + wc_ForceZero(&key2, sizeof(key2)); + } return ret; } diff --git a/tools/keytools/sign.py b/tools/keytools/sign.py deleted file mode 100755 index 75d57a100d..0000000000 --- a/tools/keytools/sign.py +++ /dev/null @@ -1,837 +0,0 @@ -#!/usr/bin/python3 -''' - * sign.py - * - * Copyright (C) 2026 wolfSSL Inc. - * - * This file is part of wolfBoot. - * - * wolfBoot is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * wolfBoot is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA -''' - -import sys, os, struct, time, re - -try: - import wolfcrypt -except: - print ("No wolfcrypt support found. Try 'pip install wolfcrypt'") - sys.exit(1) - -from wolfcrypt import ciphers, hashes - - -WOLFBOOT_MAGIC = 0x464C4F57 -HDR_END = 0x00 -HDR_VERSION = 0x01 -HDR_TIMESTAMP = 0x02 -HDR_SHA256 = 0x03 -HDR_IMG_DELTA_BASE = 0x05 -HDR_IMG_DELTA_SIZE = 0x06 -HDR_SHA3_384 = 0x13 -HDR_SHA384 = 0x14 -HDR_IMG_DELTA_INVERSE = 0x15 -HDR_IMG_DELTA_INVERSE_SIZE = 0x16 -HDR_IMG_TYPE = 0x04 -HDR_PUBKEY = 0x10 -HDR_SIGNATURE = 0x20 -HDR_PADDING = 0xFF - - -HDR_VERSION_LEN = 4 -HDR_TIMESTAMP_LEN = 8 -HDR_SHA256_LEN = 32 -HDR_SHA384_LEN = 48 -HDR_SHA3_384_LEN = 48 -HDR_IMG_TYPE_LEN = 2 -HDR_SIGNATURE_LEN = 64 - -HDR_IMG_TYPE_AUTH_NONE = 0xFF00 -HDR_IMG_TYPE_AUTH_ED25519 = 0x0100 -HDR_IMG_TYPE_AUTH_ECC256 = 0x0200 -HDR_IMG_TYPE_AUTH_RSA2048 = 0x0300 -HDR_IMG_TYPE_AUTH_RSA4096 = 0x0400 -HDR_IMG_TYPE_AUTH_ED448 = 0x0500 -HDR_IMG_TYPE_AUTH_ECC384 = 0x0600 -HDR_IMG_TYPE_AUTH_ECC521 = 0x0700 -HDR_IMG_TYPE_AUTH_RSA3072 = 0x0800 -HDR_IMG_TYPE_DIFF = 0x00D0 - -HDR_IMG_TYPE_WOLFBOOT = 0x0000 -HDR_IMG_TYPE_APP = 0x0001 - -WOLFBOOT_HEADER_SIZE = 256 -WOLFBOOT_PARTITION_SIZE = 0 -WOLFBOOT_SECTOR_SIZE = 0 - -sign="auto" -self_update=False -sha_only=False -manual_sign=False -encrypt=False -chacha=True -aes128=False -aes256=False -delta=False -encrypt_key_file=None -delta_base_file=None -partition_id = HDR_IMG_TYPE_APP - - -argc = len(sys.argv) -argv = sys.argv -hash_algo='sha256' - - -def make_header(image_file, fw_version, extra_fields=[]): - img_size = os.path.getsize(image_file) - # Magic header (spells 'WOLF') - header = struct.pack(' 0): - img_type |= HDR_IMG_TYPE_DIFF - - header += struct.pack(' 12): - print("Usage: "+argv[0]+" [options] image key version"); - print("For full usage manual, see 'docs/Signing.md'"); - sys.exit(1) - -i = 1 -while (i < len(argv)): - if (argv[i] == '--no-sign'): - sign='none' - elif (argv[i] == '--ed25519'): - sign='ed25519' - elif (argv[i] == '--ed448'): - sign='ed448' - elif (argv[i] == '--ecc256'): - sign='ecc256' - elif (argv[i] == '--ecc384'): - sign='ecc384' - elif (argv[i] == '--ecc521'): - sign='ecc521' - elif (argv[i] == '--rsa2048'): - sign='rsa2048' - elif (argv[i] == '--rsa3072'): - sign='rsa3072' - elif (argv[i] == '--rsa4096'): - sign='rsa4096' - elif (argv[i] == '--sha256'): - hash_algo='sha256' - elif (argv[i] == '--sha384'): - hash_algo='sha384' - elif (argv[i] == '--sha3'): - hash_algo='sha3' - elif (argv[i] == '--wolfboot-update'): - self_update = True - partition_id = HDR_IMG_TYPE_WOLFBOOT - elif (argv[i] == '--id'): - i+=1 - partition_id = int(argv[i]) - if partition_id < 0 or partition_id > 15: - print("Invalid partition id: " + argv[i]) - sys.exit(16) - if partition_id == 0: - self_update = True - elif (argv[i] == '--sha-only'): - sha_only = True - elif (argv[i] == '--manual-sign'): - manual_sign = True - elif (argv[i] == '--encrypt'): - encrypt = True - i += 1 - encrypt_key_file = argv[i] - elif (argv[i] == '--chacha'): - encrypt = True - elif (argv[i] == '--aes128'): - encrypt = True - chacha = False - aes128 = True - elif (argv[i] == '--aes256'): - encrypt = True - chacha = False - aes256 = True - elif (argv[i] == '--delta'): - delta = True - i += 1 - delta_base_file = argv[i] - else: - i-=1 - break - i += 1 - - -if (encrypt and delta): - print("Encryption of delta image") - -try: - cfile = open(".config", "r") -except: - cfile = None - pass - -if cfile: - l = cfile.readline() - while l != '': - if "IMAGE_HEADER_SIZE" in l: - val=l.split('=')[1].rstrip('\n') - WOLFBOOT_HEADER_SIZE = int(val,0) - print("IMAGE_HEADER_SIZE (from .config): " + str(WOLFBOOT_HEADER_SIZE)) - if "WOLFBOOT_PARTITION_SIZE" in l and "ADDRESS" not in l: - val=l.split('=')[1].rstrip('\n') - WOLFBOOT_PARTITION_SIZE = int(val,0) - if "WOLFBOOT_SECTOR_SIZE" in l: - val=l.split('=')[1].rstrip('\n') - WOLFBOOT_SECTOR_SIZE = int(val,0) - - l = cfile.readline() - cfile.close() - - -image_file = argv[i+1] -if sign != 'none': - key_file = argv[i+2] - fw_version = int(argv[i+3]) -else: - key_file = '' - fw_version = int(argv[i+2]) - -if manual_sign: - signature_file = argv[i+4] - -if not sha_only: - if '.' in image_file: - tokens = image_file.split('.') - output_image_file = '' - for x in tokens[0:-1]: - output_image_file+=x - output_image_file += "_v" + str(fw_version) + "_signed.bin" - else: - output_image_file = image_file + "_v" + str(fw_version) + "_signed.bin" -else: - if '.' in image_file: - tokens = image_file.split('.') - output_image_file = '' - for x in tokens[0:-1]: - output_image_file+=x - output_image_file += "_v" + str(fw_version) + "_digest.bin" - else: - output_image_file = image_file + "_v" + str(fw_version) + "_digest.bin" - -if delta and encrypt: - if '.' in image_file: - tokens = image_file.split('.') - encrypted_output_image_file = '' - for x in tokens[0:-1]: - encrypted_output_image_file += x - encrypted_output_image_file += "_v" + str(fw_version) + "_signed_diff_encrypted.bin" - else: - encrypted_output_image_file = image_file + "_v" + str(fw_version) + "_signed_diff_encrypted.bin" - -elif encrypt: - if '.' in image_file: - tokens = image_file.split('.') - encrypted_output_image_file = '' - for x in tokens[0:-1]: - encrypted_output_image_file += x - encrypted_output_image_file += "_v" + str(fw_version) + "_signed_and_encrypted.bin" - else: - encrypted_output_image_file = image_file + "_v" + str(fw_version) + "_signed_and_encrypted.bin" - -if delta: - if '.' in image_file: - tokens = image_file.split('.') - delta_output_image_file = '' - for x in tokens[0:-1]: - delta_output_image_file += x - delta_output_image_file += "_v" + str(fw_version) + "_signed_diff.bin" - else: - delta_output_image_file = image_file + "_v" + str(fw_version) + "_signed_diff.bin" - -if (self_update): - print("Update type: wolfBoot") -else: - print("Update type: Firmware") - -print ("Input image: " + image_file) - -print ("Selected cipher: " + sign) -print ("Private key: " + key_file) - -if not sha_only: - print ("Output image: " + output_image_file) -else: - print ("Output digest: " + output_image_file) - -if not encrypt: - print ("Not Encrypted") -else: - print ("Encrypted using: " + encrypt_key_file) -nickname = "" -if partition_id == 0: - nickname = "(bootloader)" -print ("Target partition id: " + str(partition_id) +" "+ nickname) - -if sign == 'none': - kf = None - wolfboot_key_buffer='' - wolfboot_key_buffer_len = 0 -else: - kf = open(key_file, "rb") - wolfboot_key_buffer = kf.read(4096) - wolfboot_key_buffer_len = len(wolfboot_key_buffer) - -if wolfboot_key_buffer_len == 0: - if (sign != 'none'): - print("Error. Key size is zero but cipher is " + sign) - sys.exit(3) - print("*** WARNING: cipher 'none' selected.") - print("*** Image will not be authenticated!") - print("*** SECURE BOOT DISABLED.") - -elif wolfboot_key_buffer_len == 32: - if (sign != 'ed25519' and not manual_sign and not sha_only): - print("Error: key too short for cipher") - sys.exit(1) - elif sign == 'auto' and (manual_sign or sha_only): - sign = 'ed25519' - print("'ed25519' public key autodetected.") -elif wolfboot_key_buffer_len == 64: - if (sign == 'ecc256'): - if not manual_sign and not sha_only: - print("Error: key size does not match the cipher selected") - sys.exit(1) - else: - print("Ecc256 public key detected") - if sign == 'auto': - if (manual_sign or sha_only): - sign = 'ecc256' - print("'ecc256' public key autodetected.") - else: - sign = 'ed25519' - print("'ed25519' key autodetected.") -elif wolfboot_key_buffer_len == 114: - if (sign != 'ed448' and not manual_sign and not sha_only): - print("Error: key size incorrect for cipher") - sys.exit(1) - elif sign == 'auto' and (manual_sign or sha_only): - sign = 'ed448' - print("'ed448' public key autodetected.") -elif wolfboot_key_buffer_len == 96: - if (sign == 'ed25519'): - print("Error: key size does not match the cipher selected") - sys.exit(1) - if sign == 'auto': - sign = 'ecc256' - print("'ecc256' key autodetected.") -elif wolfboot_key_buffer_len == 144: - if (sign != 'auto' and sign != 'ecc384'): - print("Error: key size does not match the cipher selected") - sys.exit(1) - if sign == 'auto': - sign = 'ecc384' - print("'ecc384' key autodetected.") -elif wolfboot_key_buffer_len == 198: - if (sign != 'auto' and sign != 'ecc521'): - print("Error: key size does not match the cipher selected") - sys.exit(1) - if sign == 'auto': - sign = 'ecc521' - print("'ecc521' key autodetected.") -elif (wolfboot_key_buffer_len > 512): - if (sign == 'auto'): - sign = 'rsa4096' - print("'rsa4096' key autodetected.") -elif (wolfboot_key_buffer_len > 256): - if (sign == 'auto'): - sign = 'rsa3072' - print("'rsa3072' key autodetected.") -elif (wolfboot_key_buffer_len > 128): - if (sign == 'auto'): - sign = 'rsa2048' - print("'rsa2048' key autodetected.") - elif (sign != 'rsa2048'): - print ("Error: key size %d too large for the selected cipher" % wolfboot_key_buffer_len) -else: - if sign[0:3] == 'ecc': - # if this decode doesn't raise an error we have a valid ecc key - # public only - if manual_sign or sha_only: - tmpEcc = ciphers.EccPublic(wolfboot_key_buffer) - #private - else: - tmpEcc = ciphers.EccPrivate() - tmpEcc.decode_key(wolfboot_key_buffer) - else: - print ("Error: key size does not match any cipher") - sys.exit(2) - -if sign == 'none': - privkey = None - pubkey = None -elif not sha_only and not manual_sign: - ''' import (decode) private key for signing ''' - if sign == 'ed25519': - ed = ciphers.Ed25519Private(key = wolfboot_key_buffer) - privkey, pubkey = ed.encode_key() - - if sign == 'ed448': - HDR_SIGNATURE_LEN = 114 - if WOLFBOOT_HEADER_SIZE < 512: - print("Ed448: header size increased to 512") - WOLFBOOT_HEADER_SIZE = 512 - ed = ciphers.Ed448Private(key = wolfboot_key_buffer) - privkey, pubkey = ed.encode_key() - - if sign == 'ecc256': - ecc = ciphers.EccPrivate() - - if (wolfboot_key_buffer_len == 96): - ecc.decode_key_raw(wolfboot_key_buffer[0:32], - wolfboot_key_buffer[32:64], wolfboot_key_buffer[64:]) - pubkey = wolfboot_key_buffer[0:64] - else: - ecc.decode_key(wolfboot_key_buffer) - pubkey = ecc.encode_key_raw() - pubkey = pubkey[0] + pubkey[1] - - if sign == 'ecc384': - HDR_SIGNATURE_LEN = 96 - if WOLFBOOT_HEADER_SIZE < 512: - print("Ecc384: header size increased to 512") - WOLFBOOT_HEADER_SIZE = 512 - ecc = ciphers.EccPrivate() - - if (wolfboot_key_buffer_len == 144): - ecc.decode_key_raw(wolfboot_key_buffer[0:48], - wolfboot_key_buffer[48:96], wolfboot_key_buffer[96:], - curve_id = ciphers.ECC_SECP384R1) - pubkey = wolfboot_key_buffer[0:96] - else: - ecc.decode_key(wolfboot_key_buffer) - pubkey = ecc.encode_key_raw() - pubkey = pubkey[0] + pubkey[1] - - - if sign == 'ecc521': - HDR_SIGNATURE_LEN = 132 - - ecc = ciphers.EccPrivate() - - if (wolfboot_key_buffer_len == 198): - ecc.decode_key_raw(wolfboot_key_buffer[0:66], - wolfboot_key_buffer[66:132], wolfboot_key_buffer[132:], - curve_id = ciphers.ECC_SECP521R1) - pubkey = wolfboot_key_buffer[0:132] - else: - ecc.decode_key(wolfboot_key_buffer) - pubkey = ecc.encode_key_raw() - pubkey = pubkey[0] + pubkey[1] - - if WOLFBOOT_HEADER_SIZE < 512: - print("Ecc521: header size increased to 512") - WOLFBOOT_HEADER_SIZE = 512 - - if sign == 'rsa2048': - if WOLFBOOT_HEADER_SIZE < 512: - print("Rsa2048: header size increased to 512") - WOLFBOOT_HEADER_SIZE = 512 - HDR_SIGNATURE_LEN = 256 - rsa = ciphers.RsaPrivate(wolfboot_key_buffer) - privkey,pubkey = rsa.encode_key() - - if sign == 'rsa3072': - if hash_algo != 'sha256': - if WOLFBOOT_HEADER_SIZE < 1024: - print("Rsa3072: header size increased to 1024") - WOLFBOOT_HEADER_SIZE = 1024 - if WOLFBOOT_HEADER_SIZE < 512: - print("Rsa3072: header size increased to 512") - WOLFBOOT_HEADER_SIZE = 512 - HDR_SIGNATURE_LEN = 384 - rsa = ciphers.RsaPrivate(wolfboot_key_buffer) - privkey,pubkey = rsa.encode_key() - - if sign == 'rsa4096': - if WOLFBOOT_HEADER_SIZE < 1024: - print("Rsa4096: header size increased to 1024") - WOLFBOOT_HEADER_SIZE = 1024 - HDR_SIGNATURE_LEN = 512 - rsa = ciphers.RsaPrivate(wolfboot_key_buffer) - privkey,pubkey = rsa.encode_key() - -else: - if sign == 'rsa2048': - if WOLFBOOT_HEADER_SIZE < 512: - WOLFBOOT_HEADER_SIZE = 512 - HDR_SIGNATURE_LEN = 256 - if sign == 'rsa3072': - if WOLFBOOT_HEADER_SIZE < 512: - WOLFBOOT_HEADER_SIZE = 512 - HDR_SIGNATURE_LEN = 384 - if sign == 'rsa4096': - if WOLFBOOT_HEADER_SIZE < 1024: - WOLFBOOT_HEADER_SIZE = 1024 - HDR_SIGNATURE_LEN = 512 - - # if it's an ecc key, check if it is encoded - if (sign == 'ecc256' and wolfboot_key_buffer_len != 64) or (sign == 'ecc384' and wolfboot_key_buffer_len != 96) or (sign == 'ecc384' and wolfboot_key_buffer_len != 132): - eccKey = ciphers.EccPublic(wolfboot_key_buffer) - pubkey = eccKey.encode_key_raw() - pubkey = pubkey[0] + pubkey[1] - else: - pubkey = wolfboot_key_buffer - -header = make_header(image_file, fw_version) - -# Create output image. Add padded header in front -outfile = open(output_image_file, 'wb') -outfile.write(header) -sz = len(header) -while sz < WOLFBOOT_HEADER_SIZE: - outfile.write(struct.pack('B',HDR_PADDING)) - sz += 1 -infile = open(image_file, 'rb') -while True: - buf = infile.read(1024) - if len(buf) == 0: - break - outfile.write(buf) - -infile.close() -outfile.close() - -# Check if signed image fits in partition -if WOLFBOOT_PARTITION_SIZE > 0: - img_size = os.path.getsize(image_file) - total_img_sz = WOLFBOOT_HEADER_SIZE + img_size - # Only subtract sector for trailer when sector < partition. - # When sector >= partition (e.g. update_ram targets), the - # entire partition is available for the image. - if WOLFBOOT_SECTOR_SIZE < WOLFBOOT_PARTITION_SIZE: - max_img_sz = WOLFBOOT_PARTITION_SIZE - WOLFBOOT_SECTOR_SIZE - else: - max_img_sz = WOLFBOOT_PARTITION_SIZE - if total_img_sz > max_img_sz: - if WOLFBOOT_SECTOR_SIZE < WOLFBOOT_PARTITION_SIZE: - print("Error: Image size %d (header %d + firmware %d) " - "exceeds max %d (partition %d - sector %d)" % - (total_img_sz, WOLFBOOT_HEADER_SIZE, img_size, - max_img_sz, WOLFBOOT_PARTITION_SIZE, WOLFBOOT_SECTOR_SIZE)) - else: - print("Error: Image size %d (header %d + firmware %d) " - "exceeds max %d (partition %d)" % - (total_img_sz, WOLFBOOT_HEADER_SIZE, img_size, - max_img_sz, WOLFBOOT_PARTITION_SIZE)) - sys.exit(1) - -if (encrypt): - delta_align=64 -else: - delta_align=16 - -if (delta): - tmp_outfile='/tmp/delta.bin' - tmp_inv_outfile='/tmp/delta-1.bin' - os.system('tools/delta/bmdiff ' + delta_base_file + ' ' + output_image_file + ' ' + tmp_outfile) - os.system('tools/delta/bmdiff ' + output_image_file + ' ' + delta_base_file + ' ' + tmp_inv_outfile) - - delta_size = os.path.getsize(tmp_outfile) - delta_inv_size = os.path.getsize(tmp_inv_outfile) - delta_file = open(tmp_outfile, 'ab+') - delta_inv_file = open(tmp_inv_outfile, 'rb') - while delta_file.tell() % delta_align != 0: - delta_file.write(struct.pack('B', 0x00)) - inv_off = delta_file.tell() - while True: - cpbuf = delta_inv_file.read(1024) - if len(cpbuf) == 0: - break - delta_file.write(cpbuf) - delta_file.close() - delta_inv_file.close() - base_version = re.split("_", (re.split("_v", delta_base_file)[1]))[0] - header = make_header(tmp_outfile, fw_version, - [[HDR_IMG_DELTA_BASE, 4, struct.pack("= rx_script_len) return 0; + /* Empty polls before this script byte is delivered */ + if (rx_delay[rx_script_pos] > 0) { + rx_delay[rx_script_pos]--; + return 0; + } + *c = rx_script[rx_script_pos++]; return 1; } @@ -47,6 +54,7 @@ static void reset_uart_script(const uint8_t *script, int len) memcpy(rx_script, script, len); rx_script_len = len; rx_script_pos = 0; + memset(rx_delay, 0, sizeof(rx_delay)); memset(tx_log, 0, sizeof(tx_log)); tx_log_len = 0; } @@ -69,12 +77,65 @@ START_TEST(test_ext_flash_read_timeout_returns_error) } END_TEST +START_TEST(test_ext_flash_erase_success) +{ + uint8_t script[11]; + int ret; + + /* 10 command ACKs + the erase-completion ACK */ + memset(script, CMD_ACK, sizeof(script)); + reset_uart_script(script, sizeof(script)); + + ret = ext_flash_erase(0x1000, 0x1000); + + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(rx_script_pos, 11); +} +END_TEST + +START_TEST(test_ext_flash_erase_ack_late_budget) +{ + uint8_t script[11]; + int ret; + + /* 10 command ACKs + the erase-completion ACK, which arrives only + * after more than WAIT_CYCLES empty polls: the pre-PR short budget + * would time out here, the extended one must not. */ + memset(script, CMD_ACK, sizeof(script)); + reset_uart_script(script, sizeof(script)); + rx_delay[10] = WAIT_CYCLES + 1; + + ret = ext_flash_erase(0x1000, 0x1000); + + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(rx_script_pos, 11); +} +END_TEST + +START_TEST(test_ext_flash_erase_timeout_returns_error) +{ + uint8_t script[10]; + int ret; + + /* Command ACKs only: the erase-completion ACK never arrives */ + memset(script, CMD_ACK, sizeof(script)); + reset_uart_script(script, sizeof(script)); + + ret = ext_flash_erase(0x1000, 0x1000); + + ck_assert_int_eq(ret, -1); +} +END_TEST + Suite *wolfboot_suite(void) { Suite *s = suite_create("wolfBoot"); TCase *uart_flash = tcase_create("UART flash"); tcase_add_test(uart_flash, test_ext_flash_read_timeout_returns_error); + tcase_add_test(uart_flash, test_ext_flash_erase_success); + tcase_add_test(uart_flash, test_ext_flash_erase_ack_late_budget); + tcase_add_test(uart_flash, test_ext_flash_erase_timeout_returns_error); tcase_set_timeout(uart_flash, 20); suite_add_tcase(s, uart_flash);