Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions boot/bootutil/include/bootutil/boot_store_enc_keys.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2026 WIKA Alexander Wiegand SE & Co. KG
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _BOOT_STORE_ENC_KEYS_H_
#define _BOOT_STORE_ENC_KEYS_H_

#include <stddef.h>
#include <stdbool.h>

/**
* @brief Store the private key into the flash partition
*
* @param key Pointer to the buffer where the key is stored
* @param key_len Size of the key
*
* @return 0 on success; nonzero on failure.
*/
int store_priv_enc_key(const unsigned char *key, size_t key_len);

/**
* @brief Check the private key encryption storage is empty.
*
* @retval true The key storage is empty.
* @retval false The key storage is not empty or the flash area could not be accessed.
*
**/
bool is_key_storage_erased();

#endif /*_BOOT_STORE_ENC_KEYS_H_*/
25 changes: 25 additions & 0 deletions boot/bootutil/include/bootutil/generate_key_pair.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2026 WIKA Alexander Wiegand SE & Co. KG
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __GENERATE_KEY_PAIR_H__
#define __GENERATE_KEY_PAIR_H__

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Generate public and private key for encryption in (PKCS8 and PEM format).
*
* @return On failure, print error message.
*
*/
void generate_enc_key_pair(void);

#ifdef __cplusplus
}
#endif

#endif /* __GENERATE_KEY_PAIR_H__ */
61 changes: 61 additions & 0 deletions boot/bootutil/src/boot_store_enc_keys.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2026 WIKA Alexander Wiegand SE & Co. KG
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mcuboot_config/mcuboot_config.h"

#if defined(MCUBOOT_GEN_ENC_KEY)
#include <bootutil/boot_store_enc_keys.h>
#include "bootutil_priv.h"
#include <bootutil/bootutil_macros.h>

#define PRIV_ENC_KEY_ALIGN_SIZE ALIGN_UP(MCUBOOT_PRIV_ENC_KEY_LEN, MCUBOOT_BOOT_MAX_ALIGN)

int
store_priv_enc_key(const unsigned char *key, size_t key_len)
{
if((key == NULL) || key_len != MCUBOOT_PRIV_ENC_KEY_LEN)
{
return -1;
}

const struct flash_area *fa;
int rc = flash_area_open(KEY_STORAGE_ID, &fa);
if (rc)
{
return rc;
}

uint32_t pad_off = ALIGN_UP(KEY_STORAGE_BASE, MCUBOOT_BOOT_MAX_ALIGN) - KEY_STORAGE_BASE;
uint8_t erased_val = flash_area_erased_val(fa);

rc = boot_erase_region(fa, pad_off, PRIV_ENC_KEY_ALIGN_SIZE, false);
if (rc != 0) {
return BOOT_EFLASH;
}

uint8_t priv_enc_key[PRIV_ENC_KEY_ALIGN_SIZE];
memset(&priv_enc_key[0], erased_val, PRIV_ENC_KEY_ALIGN_SIZE);
memcpy(&priv_enc_key[0], key, MCUBOOT_PRIV_ENC_KEY_LEN);

rc = flash_area_write(fa, pad_off, &priv_enc_key[0], PRIV_ENC_KEY_ALIGN_SIZE);
flash_area_close(fa);
if (rc != 0) {
return BOOT_EFLASH;
}
return 0;
}

bool is_key_storage_erased(){
const struct flash_area *fa;
if (flash_area_open(KEY_STORAGE_ID, &fa) < 0)
{
return false;
}
bool ret = bootutil_buffer_is_erased(fa, (const void*)ALIGN_UP(KEY_STORAGE_BASE, MCUBOOT_BOOT_MAX_ALIGN), MCUBOOT_PRIV_ENC_KEY_LEN);
flash_area_close(fa);
return ret;
}

#endif /* MCUBOOT_GEN_ENC_KEY */
153 changes: 153 additions & 0 deletions boot/bootutil/src/generate_key_pair.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright (c) 2026 WIKA Alexander Wiegand SE & Co. KG
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mcuboot_config/mcuboot_config.h"

#if defined(MCUBOOT_GEN_ENC_KEY)
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/pk.h"
#include "mbedtls/ecp.h"
#include "bootutil/generate_key_pair.h"
#include "bootutil/boot_store_enc_keys.h"
#include "pkcs8secp256write.h"
#include "bootutil/bootutil_log.h"
BOOT_LOG_MODULE_DECLARE(mcuboot);

#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES (30 + 2 * MBEDTLS_ECP_MAX_BYTES)
#define PK_ECP_SIZE_BASE64 ((MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES*4+3-1)/3)
#define PEM_BEGIN_PUBLIC_KEY_SIZE 25
#define PEM_END_PUBLIC_KEY_SIZE 27
#define NEW_LINE_CHARS 3
#define PEM_PUBLIC_KEY_SIZE (PEM_BEGIN_PUBLIC_KEY_SIZE + PK_ECP_SIZE_BASE64 + PEM_END_PUBLIC_KEY_SIZE + NEW_LINE_CHARS)

/**
* @brief Generate public and private key and contain in mbedtls_pk_context.
*
* @param pk Initialize mbedtls_pk_context and contains the generate key pair.
*
* @return 0 for Success.
* @return Not equal to zero, therefore failure.
*/
static int
gen_p256_keypair(mbedtls_pk_context *pk)
{
int ret;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
const unsigned char pers[] = "keyge-p256-keygenkeyge-p256-keygen";

mbedtls_pk_init(pk);
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);

ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
pers, sizeof(pers) - 1);
if (ret != 0) {
BOOT_LOG_ERR("SEED FAIL ret=%d", ret);
goto cleanup;
}

ret = mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
if (ret != 0) {
BOOT_LOG_ERR("PK_SETUP FAIL ret=%d", ret);
goto cleanup;
}

ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(*pk),
mbedtls_ctr_drbg_random, &ctr_drbg);
if (ret != 0) {
BOOT_LOG_ERR("GEN_KEY FAIL ret=%d", ret);
goto cleanup;
}

cleanup:
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
return ret;
}

/**
* @brief Export private key in PKCS8 format.
*
* @param pk Initialize mbedtls_pk_context and contains the generate key pair.
*
* @return 0 for Success.
* @return Not equal to zero, therefore failure.
*/
static int
save_privkey_der(const mbedtls_pk_context *pk)
{
unsigned char buf[MCUBOOT_PRIV_ENC_KEY_LEN];

int len = mbedtls_pk_write_keypkcs8_der(pk, buf, sizeof(buf));
if (len != MCUBOOT_PRIV_ENC_KEY_LEN) {
BOOT_LOG_ERR("fails write pkcs8 der: len=%d", len);
return len;
}

int ret = store_priv_enc_key(buf, len);
if (ret != 0)
{
BOOT_LOG_ERR("key store fail store ret=%d",ret);
return ret;
}

return 0;
}

/**
* @brief Export private and public key in PEM format.
*
* @param pk Initialize mbedtls_pk_context and contains the generate key pair.
*
* @return 0 for Success.
* @return Not equal to zero, therefore failure.
*/
static int
export_pub_pem(const mbedtls_pk_context *pk)
{
unsigned char buf_pub[PEM_PUBLIC_KEY_SIZE];

int ret = mbedtls_pk_write_pubkey_pem(pk, buf_pub, sizeof(buf_pub));
if (ret != 0)
{
BOOT_LOG_ERR("PUBKEY_PEM FAIL ret=%d", ret);
return ret;
}

BOOT_LOG_INF("\n%s", buf_pub);

return 0;
}

void
generate_enc_key_pair(void)
{
mbedtls_pk_context pk;
BOOT_LOG_INF("Generate enc key pair starting...");

int ret = gen_p256_keypair(&pk);
if(ret != 0){
BOOT_LOG_ERR("Error during the generation enc key pair");
return;
}

ret = save_privkey_der(&pk);
if(ret != 0){
BOOT_LOG_ERR("Error during safe of private key der");
return;
}

BOOT_LOG_INF("Saved private enc key");

ret = export_pub_pem(&pk);
if(ret != 0){
BOOT_LOG_ERR("Error during the exportation of public key pem");
return;
}
}

#endif /* MCUBOOT_GEN_ENC_KEY */
40 changes: 40 additions & 0 deletions boot/zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#
# Copyright (c) 2017 Open Source Foundries Limited
# Copyright (c) 2023-2026 Nordic Semiconductor ASA
# Copyright (c) 2026 WIKA Alexander Wiegand SE & Co. KG
#
# SPDX-License-Identifier: Apache-2.0

Expand Down Expand Up @@ -36,6 +37,8 @@ if(NOT CONFIG_MBEDTLS_BUILTIN AND NOT CONFIG_BOOT_KEY_IMPORT_BYPASS_ASN)
assert_exists(MBEDTLS_ASN1_DIR)
endif()
set(NRF_DIR "${MCUBOOT_DIR}/ext/nrf")
set(MBEDTLS_PKCS8_DIR "${MCUBOOT_DIR}/ext/mbedtls-pkcs8")
assert_exists(MBEDTLS_PKCS8_DIR)

if(CONFIG_BOOT_USE_NRF_CC310_BL)
if(NOT ZEPHYR_NRFXLIB_MODULE_DIR OR NOT EXISTS ${ZEPHYR_NRFXLIB_MODULE_DIR})
Expand Down Expand Up @@ -545,6 +548,43 @@ if(CONFIG_MULTIPLE_EXECUTABLE_RAM_REGIONS_DEFAULT_FILE)
zephyr_sources(ram_load.c)
endif()

if(CONFIG_BOOT_GEN_ENC_KEY)
dt_chosen(chosen_key_storage PROPERTY "zephyr,key-storage")
if(DEFINED chosen_key_storage)
dt_get_nvm_device(${chosen_key_storage} key_storage_device)
dt_chosen(chosen_code_partition PROPERTY "zephyr,code-partition")
dt_get_nvm_device(${chosen_code_partition} code_partition_device)

if("${key_storage_device}" STREQUAL "${code_partition_device}")
dt_prop(key_storage_label PATH "${chosen_key_storage}" PROPERTY "label")
if(key_storage_label STREQUAL "storage")
message(WARNING "Using default storage. Replace with a proper partition.")
endif()

dt_prop(key_storage_adr PATH "${chosen_key_storage}" PROPERTY "reg" INDEX 0)
dt_prop(key_storage_size PATH "${chosen_key_storage}" PROPERTY "reg" INDEX 1)
align_up(${CONFIG_BOOT_PRIV_ENC_KEY_LEN} ${CONFIG_MCUBOOT_BOOT_MAX_ALIGN} priv_enc_key_size_align)
align_up(${key_storage_adr} ${CONFIG_MCUBOOT_BOOT_MAX_ALIGN} key_storage_adr_align)
math(EXPR key_storage_max_adr "${key_storage_adr} + ${key_storage_size}")
math(EXPR key_storage_required_max_adr "${key_storage_adr_align} + ${priv_enc_key_size_align}")
if (${key_storage_max_adr} LESS ${key_storage_required_max_adr})
message(FATAL_ERROR "The 'zephyr,key-storage' is to small to store priv enc key!")
endif()
else()
message(FATAL_ERROR "The 'zephyr,key-storage' must be located on same nvm device as 'zephyr,code-partition'!")
endif()
else()
message(FATAL_ERROR "The 'zephyr,key-storage' chosen property is not defined!")
endif()

zephyr_include_directories(${MBEDTLS_PKCS8_DIR})
zephyr_library_sources(
${BOOT_DIR}/bootutil/src/generate_key_pair.c
${MBEDTLS_PKCS8_DIR}/pkcs8secp256write.c
${BOOT_DIR}/bootutil/src/boot_store_enc_keys.c
)
endif()

if(SYSBUILD)
if(CONFIG_SINGLE_APPLICATION_SLOT OR CONFIG_BOOT_FIRMWARE_LOADER OR CONFIG_BOOT_SWAP_USING_SCRATCH OR CONFIG_BOOT_SWAP_USING_MOVE OR CONFIG_BOOT_SWAP_USING_OFFSET OR CONFIG_BOOT_UPGRADE_ONLY OR CONFIG_BOOT_DIRECT_XIP OR CONFIG_BOOT_RAM_LOAD)
# TODO: RAM LOAD support
Expand Down
18 changes: 18 additions & 0 deletions boot/zephyr/Kconfig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (c) 2017-2020 Linaro Limited
# Copyright (c) 2020 Arm Limited
# Copyright (c) 2023 Nordic Semiconductor ASA
# Copyright (c) 2026 WIKA Alexander Wiegand SE & Co. KG
#
# SPDX-License-Identifier: Apache-2.0
#
Expand Down Expand Up @@ -136,6 +137,16 @@ endif # BOOT_USE_PSA_CRYPTO

menu "MCUBoot settings"

config BOOT_GEN_ENC_KEY
bool "Device generate encryption key"
select BOOT_ENCRYPT_IMAGE

if BOOT_GEN_ENC_KEY
config BOOT_PRIV_ENC_KEY_LEN
int "Private encryption key length"
default 138
endif

config BOOT_SOMETHING_USES_SHA256
bool
help
Expand Down Expand Up @@ -271,6 +282,7 @@ if BOOT_SIGNATURE_TYPE_ECDSA_P256

choice BOOT_ECDSA_IMPLEMENTATION
prompt "Ecdsa implementation"
default BOOT_ECDSA_MBEDTLS if BOOT_GEN_ENC_KEY
default BOOT_ECDSA_TINYCRYPT

config BOOT_ECDSA_TINYCRYPT
Expand All @@ -290,6 +302,11 @@ config BOOT_ECDSA_MBEDTLS
select MBEDTLS_MD if MBEDTLS_BUILTIN
select MBEDTLS_ECDH_C if MBEDTLS_BUILTIN && BOOT_ENCRYPT_IMAGE
select BOOT_AES_MBEDTLS_DEPENDENCIES if MBEDTLS_BUILTIN && BOOT_ENCRYPT_IMAGE
select MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED if MBEDTLS_BUILTIN && BOOT_GEN_ENC_KEY
select MBEDTLS_PK_WRITE_C if MBEDTLS_BUILTIN && BOOT_GEN_ENC_KEY
select MBEDTLS_ENTROPY_C if MBEDTLS_BUILTIN && BOOT_GEN_ENC_KEY
select MBEDTLS_PEM_CERTIFICATE_FORMAT if MBEDTLS_BUILTIN && BOOT_GEN_ENC_KEY


config BOOT_ECDSA_CC310
bool "Use CC310"
Expand Down Expand Up @@ -803,6 +820,7 @@ endif # BOOT_ENCRYPT_X25519 && BOOT_USE_PSA_CRYPTO
config BOOT_ENCRYPTION_KEY_FILE
string "Encryption key file"
depends on BOOT_ENCRYPT_IMAGE
default "" if BOOT_GEN_ENC_KEY
default "enc-rsa2048-priv.pem" if BOOT_ENCRYPT_RSA
default "enc-ec256-priv.pem" if BOOT_ENCRYPT_EC256
default "enc-x25519-priv.pem" if BOOT_ENCRYPT_X25519
Expand Down
8 changes: 8 additions & 0 deletions boot/zephyr/gen_enc_keys.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y

CONFIG_BOOT_GEN_ENC_KEY=y
CONFIG_BOOT_ENCRYPT_IMAGE=y

CONFIG_TEST_RANDOM_GENERATOR=y
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test random number generator is not appropriate for generating keys. It is dangerous to enable this even for an example, likewise with the timer. None of this code makes any sense without a true RNG source on the target.

CONFIG_TIMER_RANDOM_GENERATOR=y
Loading
Loading