Skip to content

Commit baf382e

Browse files
During the first boot, the microcontroller automatically generates a key pair. The process includes:
1. Secure random number generation using hardware TRNG (True Random Generator). 2. ECC key pair creation (SECP256R1) using the mbedTLS library. 3. Conversion of the private key to DER format following the PKCS#8 standard. 4. Store the private key into the flash partition. 5. Conversion of the public key to PEM format for export. The private key remains stored in a secure area of the microcontroller and is never exposed. The public key can be retrived only on boot if the key storage is empty. Signed-off-by: WIKA IIoT RD <60038@wika.com>
1 parent a953b36 commit baf382e

15 files changed

Lines changed: 833 additions & 1 deletion

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (c) 2026 WIKA Alexander Wiegand SE & Co. KG
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#ifndef _BOOT_STORE_ENC_KEYS_H_
7+
#define _BOOT_STORE_ENC_KEYS_H_
8+
9+
#include <stddef.h>
10+
#include <stdbool.h>
11+
12+
/**
13+
* @brief Store the private key into the flash partition
14+
*
15+
* @param key Pointer to the buffer where the key is stored
16+
* @param key_len Size of the key
17+
*
18+
* @return 0 on success; nonzero on failure.
19+
*/
20+
int store_priv_enc_key(const unsigned char *key, size_t key_len);
21+
22+
/**
23+
* @brief Check the private key encryption storage is empty.
24+
*
25+
* @retval true The key storage is empty.
26+
* @retval false The key storage is not empty or the flash area could not be accessed.
27+
*
28+
**/
29+
bool is_key_storage_erased();
30+
31+
#endif /*_BOOT_STORE_ENC_KEYS_H_*/
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2026 WIKA Alexander Wiegand SE & Co. KG
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#ifndef __GENERATE_KEY_PAIR_H__
7+
#define __GENERATE_KEY_PAIR_H__
8+
9+
#ifdef __cplusplus
10+
extern "C" {
11+
#endif
12+
13+
/**
14+
* @brief Generate public and private key for encryption in (PKCS8 and PEM format).
15+
*
16+
* @return On failure, print error message.
17+
*
18+
*/
19+
void generate_enc_key_pair(void);
20+
21+
#ifdef __cplusplus
22+
}
23+
#endif
24+
25+
#endif /* __GENERATE_KEY_PAIR_H__ */
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2026 WIKA Alexander Wiegand SE & Co. KG
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#include "mcuboot_config/mcuboot_config.h"
7+
8+
#if defined(MCUBOOT_GEN_ENC_KEY)
9+
#include <bootutil/boot_store_enc_keys.h>
10+
#include "bootutil_priv.h"
11+
#include <bootutil/bootutil_macros.h>
12+
13+
#define PRIV_ENC_KEY_ALIGN_SIZE ALIGN_UP(MCUBOOT_PRIV_ENC_KEY_LEN, MCUBOOT_BOOT_MAX_ALIGN)
14+
15+
int
16+
store_priv_enc_key(const unsigned char *key, size_t key_len)
17+
{
18+
if((key == NULL) || key_len != MCUBOOT_PRIV_ENC_KEY_LEN)
19+
{
20+
return -1;
21+
}
22+
23+
const struct flash_area *fa;
24+
int rc = flash_area_open(KEY_STORAGE_ID, &fa);
25+
if (rc)
26+
{
27+
return rc;
28+
}
29+
30+
uint32_t pad_off = ALIGN_UP(KEY_STORAGE_BASE, MCUBOOT_BOOT_MAX_ALIGN) - KEY_STORAGE_BASE;
31+
uint8_t erased_val = flash_area_erased_val(fa);
32+
33+
rc = boot_erase_region(fa, pad_off, PRIV_ENC_KEY_ALIGN_SIZE, false);
34+
if (rc != 0) {
35+
return BOOT_EFLASH;
36+
}
37+
38+
uint8_t priv_enc_key[PRIV_ENC_KEY_ALIGN_SIZE];
39+
memset(&priv_enc_key[0], erased_val, PRIV_ENC_KEY_ALIGN_SIZE);
40+
memcpy(&priv_enc_key[0], key, MCUBOOT_PRIV_ENC_KEY_LEN);
41+
42+
rc = flash_area_write(fa, pad_off, &priv_enc_key[0], PRIV_ENC_KEY_ALIGN_SIZE);
43+
flash_area_close(fa);
44+
if (rc != 0) {
45+
return BOOT_EFLASH;
46+
}
47+
return 0;
48+
}
49+
50+
bool is_key_storage_erased(){
51+
const struct flash_area *fa;
52+
if (flash_area_open(KEY_STORAGE_ID, &fa) < 0)
53+
{
54+
return false;
55+
}
56+
bool ret = bootutil_buffer_is_erased(fa, (const void*)ALIGN_UP(KEY_STORAGE_BASE, MCUBOOT_BOOT_MAX_ALIGN), MCUBOOT_PRIV_ENC_KEY_LEN);
57+
flash_area_close(fa);
58+
return ret;
59+
}
60+
61+
#endif /* MCUBOOT_GEN_ENC_KEY */
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* Copyright (c) 2026 WIKA Alexander Wiegand SE & Co. KG
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#include "mcuboot_config/mcuboot_config.h"
7+
8+
#if defined(MCUBOOT_GEN_ENC_KEY)
9+
#include "mbedtls/entropy.h"
10+
#include "mbedtls/ctr_drbg.h"
11+
#include "mbedtls/pk.h"
12+
#include "mbedtls/ecp.h"
13+
#include "bootutil/generate_key_pair.h"
14+
#include "bootutil/boot_store_enc_keys.h"
15+
#include "pkcs8secp256write.h"
16+
#include "bootutil/bootutil_log.h"
17+
BOOT_LOG_MODULE_DECLARE(mcuboot);
18+
19+
#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES (30 + 2 * MBEDTLS_ECP_MAX_BYTES)
20+
#define PK_ECP_SIZE_BASE64 ((MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES*4+3-1)/3)
21+
#define PEM_BEGIN_PUBLIC_KEY_SIZE 25
22+
#define PEM_END_PUBLIC_KEY_SIZE 27
23+
#define NEW_LINE_CHARS 3
24+
#define PEM_PUBLIC_KEY_SIZE (PEM_BEGIN_PUBLIC_KEY_SIZE + PK_ECP_SIZE_BASE64 + PEM_END_PUBLIC_KEY_SIZE + NEW_LINE_CHARS)
25+
26+
/**
27+
* @brief Generate public and private key and contain in mbedtls_pk_context.
28+
*
29+
* @param pk Initialize mbedtls_pk_context and contains the generate key pair.
30+
*
31+
* @return 0 for Success.
32+
* @return Not equal to zero, therefore failure.
33+
*/
34+
static int
35+
gen_p256_keypair(mbedtls_pk_context *pk)
36+
{
37+
int ret;
38+
mbedtls_entropy_context entropy;
39+
mbedtls_ctr_drbg_context ctr_drbg;
40+
const unsigned char pers[] = "keyge-p256-keygenkeyge-p256-keygen";
41+
42+
mbedtls_pk_init(pk);
43+
mbedtls_entropy_init(&entropy);
44+
mbedtls_ctr_drbg_init(&ctr_drbg);
45+
46+
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
47+
pers, sizeof(pers) - 1);
48+
if (ret != 0) {
49+
BOOT_LOG_ERR("SEED FAIL ret=%d", ret);
50+
goto cleanup;
51+
}
52+
53+
ret = mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
54+
if (ret != 0) {
55+
BOOT_LOG_ERR("PK_SETUP FAIL ret=%d", ret);
56+
goto cleanup;
57+
}
58+
59+
ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(*pk),
60+
mbedtls_ctr_drbg_random, &ctr_drbg);
61+
if (ret != 0) {
62+
BOOT_LOG_ERR("GEN_KEY FAIL ret=%d", ret);
63+
goto cleanup;
64+
}
65+
66+
cleanup:
67+
mbedtls_ctr_drbg_free(&ctr_drbg);
68+
mbedtls_entropy_free(&entropy);
69+
return ret;
70+
}
71+
72+
/**
73+
* @brief Export private key in PKCS8 format.
74+
*
75+
* @param pk Initialize mbedtls_pk_context and contains the generate key pair.
76+
*
77+
* @return 0 for Success.
78+
* @return Not equal to zero, therefore failure.
79+
*/
80+
static int
81+
save_privkey_der(const mbedtls_pk_context *pk)
82+
{
83+
unsigned char buf[MCUBOOT_PRIV_ENC_KEY_LEN];
84+
85+
int len = mbedtls_pk_write_keypkcs8_der(pk, buf, sizeof(buf));
86+
if (len != MCUBOOT_PRIV_ENC_KEY_LEN) {
87+
BOOT_LOG_ERR("fails write pkcs8 der: len=%d", len);
88+
return len;
89+
}
90+
91+
int ret = store_priv_enc_key(buf, len);
92+
if (ret != 0)
93+
{
94+
BOOT_LOG_ERR("key store fail store ret=%d",ret);
95+
return ret;
96+
}
97+
98+
return 0;
99+
}
100+
101+
/**
102+
* @brief Export private and public key in PEM format.
103+
*
104+
* @param pk Initialize mbedtls_pk_context and contains the generate key pair.
105+
*
106+
* @return 0 for Success.
107+
* @return Not equal to zero, therefore failure.
108+
*/
109+
static int
110+
export_pub_pem(const mbedtls_pk_context *pk)
111+
{
112+
unsigned char buf_pub[PEM_PUBLIC_KEY_SIZE];
113+
114+
int ret = mbedtls_pk_write_pubkey_pem(pk, buf_pub, sizeof(buf_pub));
115+
if (ret != 0)
116+
{
117+
BOOT_LOG_ERR("PUBKEY_PEM FAIL ret=%d", ret);
118+
return ret;
119+
}
120+
121+
BOOT_LOG_INF("\n%s", buf_pub);
122+
123+
return 0;
124+
}
125+
126+
void
127+
generate_enc_key_pair(void)
128+
{
129+
mbedtls_pk_context pk;
130+
BOOT_LOG_INF("Generate enc key pair starting...");
131+
132+
int ret = gen_p256_keypair(&pk);
133+
if(ret != 0){
134+
BOOT_LOG_ERR("Error during the generation enc key pair");
135+
return;
136+
}
137+
138+
ret = save_privkey_der(&pk);
139+
if(ret != 0){
140+
BOOT_LOG_ERR("Error during safe of private key der");
141+
return;
142+
}
143+
144+
BOOT_LOG_INF("Saved private enc key");
145+
146+
ret = export_pub_pem(&pk);
147+
if(ret != 0){
148+
BOOT_LOG_ERR("Error during the exportation of public key pem");
149+
return;
150+
}
151+
}
152+
153+
#endif /* MCUBOOT_GEN_ENC_KEY */

boot/zephyr/CMakeLists.txt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#
33
# Copyright (c) 2017 Open Source Foundries Limited
44
# Copyright (c) 2023-2026 Nordic Semiconductor ASA
5+
# Copyright (c) 2026 WIKA Alexander Wiegand SE & Co. KG
56
#
67
# SPDX-License-Identifier: Apache-2.0
78

@@ -36,6 +37,8 @@ if(NOT CONFIG_MBEDTLS_BUILTIN AND NOT CONFIG_BOOT_KEY_IMPORT_BYPASS_ASN)
3637
assert_exists(MBEDTLS_ASN1_DIR)
3738
endif()
3839
set(NRF_DIR "${MCUBOOT_DIR}/ext/nrf")
40+
set(MBEDTLS_PKCS8_DIR "${MCUBOOT_DIR}/ext/mbedtls-pkcs8")
41+
assert_exists(MBEDTLS_PKCS8_DIR)
3942

4043
if(CONFIG_BOOT_USE_NRF_CC310_BL)
4144
if(NOT ZEPHYR_NRFXLIB_MODULE_DIR OR NOT EXISTS ${ZEPHYR_NRFXLIB_MODULE_DIR})
@@ -545,6 +548,43 @@ if(CONFIG_MULTIPLE_EXECUTABLE_RAM_REGIONS_DEFAULT_FILE)
545548
zephyr_sources(ram_load.c)
546549
endif()
547550

551+
if(CONFIG_BOOT_GEN_ENC_KEY)
552+
dt_chosen(chosen_key_storage PROPERTY "zephyr,key-storage")
553+
if(DEFINED chosen_key_storage)
554+
dt_get_nvm_device(${chosen_key_storage} key_storage_device)
555+
dt_chosen(chosen_code_partition PROPERTY "zephyr,code-partition")
556+
dt_get_nvm_device(${chosen_code_partition} code_partition_device)
557+
558+
if("${key_storage_device}" STREQUAL "${code_partition_device}")
559+
dt_prop(key_storage_label PATH "${chosen_key_storage}" PROPERTY "label")
560+
if(key_storage_label STREQUAL "storage")
561+
message(WARNING "Using default storage. Replace with a proper partition.")
562+
endif()
563+
564+
dt_prop(key_storage_adr PATH "${chosen_key_storage}" PROPERTY "reg" INDEX 0)
565+
dt_prop(key_storage_size PATH "${chosen_key_storage}" PROPERTY "reg" INDEX 1)
566+
align_up(${CONFIG_BOOT_PRIV_ENC_KEY_LEN} ${CONFIG_MCUBOOT_BOOT_MAX_ALIGN} priv_enc_key_size_align)
567+
align_up(${key_storage_adr} ${CONFIG_MCUBOOT_BOOT_MAX_ALIGN} key_storage_adr_align)
568+
math(EXPR key_storage_max_adr "${key_storage_adr} + ${key_storage_size}")
569+
math(EXPR key_storage_required_max_adr "${key_storage_adr_align} + ${priv_enc_key_size_align}")
570+
if (${key_storage_max_adr} LESS ${key_storage_required_max_adr})
571+
message(FATAL_ERROR "The 'zephyr,key-storage' is to small to store priv enc key!")
572+
endif()
573+
else()
574+
message(FATAL_ERROR "The 'zephyr,key-storage' must be located on same nvm device as 'zephyr,code-partition'!")
575+
endif()
576+
else()
577+
message(FATAL_ERROR "The 'zephyr,key-storage' chosen property is not defined!")
578+
endif()
579+
580+
zephyr_include_directories(${MBEDTLS_PKCS8_DIR})
581+
zephyr_library_sources(
582+
${BOOT_DIR}/bootutil/src/generate_key_pair.c
583+
${MBEDTLS_PKCS8_DIR}/pkcs8secp256write.c
584+
${BOOT_DIR}/bootutil/src/boot_store_enc_keys.c
585+
)
586+
endif()
587+
548588
if(SYSBUILD)
549589
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)
550590
# TODO: RAM LOAD support

boot/zephyr/Kconfig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Copyright (c) 2017-2020 Linaro Limited
22
# Copyright (c) 2020 Arm Limited
33
# Copyright (c) 2023 Nordic Semiconductor ASA
4+
# Copyright (c) 2026 WIKA Alexander Wiegand SE & Co. KG
45
#
56
# SPDX-License-Identifier: Apache-2.0
67
#
@@ -136,6 +137,16 @@ endif # BOOT_USE_PSA_CRYPTO
136137

137138
menu "MCUBoot settings"
138139

140+
config BOOT_GEN_ENC_KEY
141+
bool "Device generate encryption key"
142+
select BOOT_ENCRYPT_IMAGE
143+
144+
if BOOT_GEN_ENC_KEY
145+
config BOOT_PRIV_ENC_KEY_LEN
146+
int "Private encryption key length"
147+
default 138
148+
endif
149+
139150
config BOOT_SOMETHING_USES_SHA256
140151
bool
141152
help
@@ -271,6 +282,7 @@ if BOOT_SIGNATURE_TYPE_ECDSA_P256
271282

272283
choice BOOT_ECDSA_IMPLEMENTATION
273284
prompt "Ecdsa implementation"
285+
default BOOT_ECDSA_MBEDTLS if BOOT_GEN_ENC_KEY
274286
default BOOT_ECDSA_TINYCRYPT
275287

276288
config BOOT_ECDSA_TINYCRYPT
@@ -290,6 +302,11 @@ config BOOT_ECDSA_MBEDTLS
290302
select MBEDTLS_MD if MBEDTLS_BUILTIN
291303
select MBEDTLS_ECDH_C if MBEDTLS_BUILTIN && BOOT_ENCRYPT_IMAGE
292304
select BOOT_AES_MBEDTLS_DEPENDENCIES if MBEDTLS_BUILTIN && BOOT_ENCRYPT_IMAGE
305+
select MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED if MBEDTLS_BUILTIN && BOOT_GEN_ENC_KEY
306+
select MBEDTLS_PK_WRITE_C if MBEDTLS_BUILTIN && BOOT_GEN_ENC_KEY
307+
select MBEDTLS_ENTROPY_C if MBEDTLS_BUILTIN && BOOT_GEN_ENC_KEY
308+
select MBEDTLS_PEM_CERTIFICATE_FORMAT if MBEDTLS_BUILTIN && BOOT_GEN_ENC_KEY
309+
293310

294311
config BOOT_ECDSA_CC310
295312
bool "Use CC310"
@@ -803,6 +820,7 @@ endif # BOOT_ENCRYPT_X25519 && BOOT_USE_PSA_CRYPTO
803820
config BOOT_ENCRYPTION_KEY_FILE
804821
string "Encryption key file"
805822
depends on BOOT_ENCRYPT_IMAGE
823+
default "" if BOOT_GEN_ENC_KEY
806824
default "enc-rsa2048-priv.pem" if BOOT_ENCRYPT_RSA
807825
default "enc-ec256-priv.pem" if BOOT_ENCRYPT_EC256
808826
default "enc-x25519-priv.pem" if BOOT_ENCRYPT_X25519

boot/zephyr/gen_enc_keys.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y
3+
4+
CONFIG_BOOT_GEN_ENC_KEY=y
5+
CONFIG_BOOT_ENCRYPT_IMAGE=y
6+
7+
CONFIG_TEST_RANDOM_GENERATOR=y
8+
CONFIG_TIMER_RANDOM_GENERATOR=y

0 commit comments

Comments
 (0)