Skip to content

Commit b333317

Browse files
authored
Merge pull request #510 from danielinux/hybrid-auth
Support for hybrid authentication (two ciphers)
2 parents c4f87c6 + 195f479 commit b333317

File tree

23 files changed

+1289
-648
lines changed

23 files changed

+1289
-648
lines changed

.github/workflows/test-sunnyday-simulator.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,3 +558,20 @@ jobs:
558558
run: |
559559
tools/scripts/sim-pq-sunnyday-update.sh config/examples/sim-ml-dsa.config
560560
561+
# 64 Bit simulator, Hybrid auth ML_DSA + ECDSA
562+
#
563+
- name: make clean
564+
run: |
565+
make distclean
566+
567+
- name: Select config (64 bit simulator) Hybrid ML_DSA + ECC
568+
run: |
569+
cp config/examples/sim-ml-dsa-ecc-hybrid.config .config
570+
571+
- name: Build wolfboot.elf
572+
run: |
573+
make clean && make
574+
575+
- name: Run sunny day hybrid boot test
576+
run: |
577+
./wolfboot.elf get_version

.github/workflows/test-tpm.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ jobs:
113113
with:
114114
arch: host
115115
config-file: ./config/examples/sim-tpm-seal.config
116-
make-args: SIGN=ECC256 HASH=SHA256 POLICY_FILE=policy.bin
116+
make-args: SIGN=ECC256 HASH=SHA256 POLICY_FILE=policy.bin IMAGE_HEADER_SIZE=512
117117
keyauthstr: TestAuth
118118
sealauthstr: SealAuth
119119

@@ -143,7 +143,7 @@ jobs:
143143
with:
144144
arch: host
145145
config-file: ./config/examples/sim-tpm-seal.config
146-
make-args: SIGN=ECC256 HASH=SHA256 POLICY_FILE=policy.bin
146+
make-args: SIGN=ECC256 HASH=SHA256 POLICY_FILE=policy.bin IMAGE_HEADER_SIZE=512
147147
keyauthstr: TestAuth
148148

149149
sim_tpm_seal_noauth_ecc384:

Makefile

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,13 @@ ifeq ($(FLASH_OTP_KEYSTORE),1)
141141
MAIN_TARGET+=tools/keytools/otp/otp-keystore-primer.bin
142142
endif
143143

144+
ifneq ($(SIGN_SECONDARY),)
145+
SECONDARY_PRIVATE_KEY=wolfboot_signing_second_private_key.der
146+
endif
147+
144148
ASFLAGS:=$(CFLAGS)
145149

146-
all: $(MAIN_TARGET)
150+
all: $(SECONDARY_PRIVATE_KEY) $(MAIN_TARGET)
147151

148152
stage1: stage1/loader_stage1.bin
149153
stage1/loader_stage1.bin: wolfboot.elf
@@ -203,6 +207,15 @@ $(PRIVATE_KEY):
203207
$(Q)(test $(SIGN) = NONE) && (echo "// SIGN=NONE" > src/keystore.c) || true
204208
$(Q)(test "$(FLASH_OTP_KEYSTORE)" = "1") && (make -C tools/keytools/otp) || true
205209

210+
$(SECONDARY_PRIVATE_KEY): $(PRIVATE_KEY) keystore.der
211+
$(Q)$(MAKE) keytools_check
212+
$(Q)rm -f src/keystore.c
213+
$(Q)dd if=keystore.der of=pubkey_1.der bs=1 skip=16
214+
$(Q)(test $(SIGN_SECONDARY) = NONE) || ("$(KEYGEN_TOOL)" \
215+
$(KEYGEN_OPTIONS) -i pubkey_1.der $(SECONDARY_KEYGEN_OPTIONS) \
216+
-g $(SECONDARY_PRIVATE_KEY)) || true
217+
$(Q)(test "$(FLASH_OTP_KEYSTORE)" = "1") && (make -C tools/keytools/otp) || true
218+
206219
keytools: include/target.h
207220
@echo "Building key tools"
208221
@$(MAKE) -C tools/keytools -s clean
@@ -220,7 +233,12 @@ swtpmtools:
220233

221234
test-app/image_v1_signed.bin: $(BOOT_IMG)
222235
@echo "\t[SIGN] $(BOOT_IMG)"
223-
$(Q)(test $(SIGN) = NONE) || "$(SIGN_TOOL)" $(SIGN_OPTIONS) $(BOOT_IMG) $(PRIVATE_KEY) 1
236+
@echo "\tSECONDARY_SIGN_OPTIONS=$(SECONDARY_SIGN_OPTIONS)"
237+
@echo "\tSECONDARY_PRIVATE_KEY=$(SECONDARY_PRIVATE_KEY)"
238+
239+
$(Q)(test $(SIGN) = NONE) || "$(SIGN_TOOL)" $(SIGN_OPTIONS) \
240+
$(SECONDARY_SIGN_OPTIONS) $(BOOT_IMG) $(PRIVATE_KEY) \
241+
$(SECONDARY_PRIVATE_KEY) 1 || true
224242
$(Q)(test $(SIGN) = NONE) && "$(SIGN_TOOL)" $(SIGN_OPTIONS) $(BOOT_IMG) 1 || true
225243

226244
test-app/image.elf: wolfboot.elf
@@ -312,7 +330,7 @@ keys: $(PRIVATE_KEY)
312330

313331
clean:
314332
$(Q)rm -f src/*.o hal/*.o hal/spi/*.o test-app/*.o src/x86/*.o
315-
$(Q)rm -f lib/wolfssl/wolfcrypt/src/*.o lib/wolfTPM/src/*.o lib/wolfTPM/hal/*.o
333+
$(Q)rm -f lib/wolfssl/wolfcrypt/src/*.o lib/wolfTPM/src/*.o lib/wolfTPM/hal/*.o lib/wolfTPM/examples/pcr/*.o
316334
$(Q)rm -f lib/wolfssl/wolfcrypt/src/port/Renesas/*.o
317335
$(Q)rm -f wolfboot.bin wolfboot.elf wolfboot.map test-update.rom wolfboot.hex
318336
$(Q)rm -f $(MACHINE_OBJ) $(MAIN_TARGET) $(LSCRIPT)
@@ -408,6 +426,8 @@ tools/keytools/otp/otp-keystore-primer.bin: FORCE
408426
make -C tools/keytools/otp clean
409427
make -C tools/keytools/otp
410428

429+
secondary: $(SECONDARY_PRIVATE_KEY)
430+
411431
%.o:%.c
412432
@echo "\t[CC-$(ARCH)] $@"
413433
$(Q)$(CC) $(CFLAGS) -c $(OUTPUT_FLAG) $@ $^

arch.mk

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,13 @@ SPI_TARGET=$(TARGET)
2525
# Default UART driver name
2626
UART_TARGET=$(TARGET)
2727

28-
# Include SHA256 module because it's implicitly needed by RSA
29-
WOLFCRYPT_OBJS+=./lib/wolfssl/wolfcrypt/src/sha256.o
28+
# Include some modules by default
29+
WOLFCRYPT_OBJS+=./lib/wolfssl/wolfcrypt/src/sha256.o \
30+
./lib/wolfssl/wolfcrypt/src/hash.o \
31+
./lib/wolfssl/wolfcrypt/src/memory.o \
32+
./lib/wolfssl/wolfcrypt/src/wc_port.o \
33+
./lib/wolfssl/wolfcrypt/src/wolfmath.o
34+
3035

3136
ifeq ($(ARCH),x86_64)
3237
CFLAGS+=-DARCH_x86_64
@@ -239,7 +244,6 @@ else
239244
CORTEXM_ARM_EXTRA_OBJS=
240245
CORTEXM_ARM_EXTRA_CFLAGS=
241246
SECURE_OBJS+=./src/wc_callable.o
242-
SECURE_OBJS+=./lib/wolfssl/wolfcrypt/src/random.o
243247
CFLAGS+=-DWOLFCRYPT_SECURE_MODE
244248
SECURE_LDFLAGS+=-Wl,--cmse-implib -Wl,--out-implib=./src/wc_secure_calls.o
245249
endif
@@ -1025,6 +1029,7 @@ ifeq ($(TARGET),sim)
10251029
LD_END_GROUP=
10261030
BOOT_IMG=test-app/image.elf
10271031
CFLAGS+=-DARCH_SIM
1032+
CFLAGS+=-DWOLFBOOT_USE_STDLIBC
10281033
ifeq ($(FORCE_32BIT),1)
10291034
CFLAGS+=-m32
10301035
LDFLAGS+=-m32
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
ARCH=sim
2+
TARGET=sim
3+
SIGN?=ML_DSA
4+
ML_DSA_LEVEL=3
5+
IMAGE_SIGNATURE_SIZE=3309
6+
IMAGE_HEADER_SIZE?=8192
7+
HASH?=SHA256
8+
WOLFBOOT_SMALL_STACK?=0
9+
SPI_FLASH=0
10+
DEBUG=0
11+
WOLFBOOT_UNIVERSAL_KEYSTORE=1
12+
SIGN_SECONDARY=ECC384
13+
14+
# sizes should be multiple of system page size
15+
WOLFBOOT_PARTITION_SIZE=0x40000
16+
WOLFBOOT_SECTOR_SIZE=0x1000
17+
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000
18+
# if on external flash, it should be multiple of system page size
19+
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x100000
20+
WOLFBOOT_PARTITION_SWAP_ADDRESS=0x180000
21+
22+
# required for keytools
23+
WOLFBOOT_FIXED_PARTITIONS=1
24+
25+
# For debugging XMALLOC/XFREE
26+
#CFLAGS_EXTRA+=-DWOLFBOOT_DEBUG_MALLOC

docs/PQ.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,109 @@ parameter set: XMSSMT-SHA2_20/2_256
178178
signature length: 4963
179179
```
180180

181+
## Hybrid mode (classic + PQ)
182+
183+
wolfBoot supports a hybrid mode where both classic and PQ signatures are verified,
184+
sequentially. This allows for a gradual transition from classic to PQ signatures,
185+
and protects the secure boot mechanism from potential vulnerabilities in either of
186+
the two ciphers in use.
187+
188+
The hybrid mode is enabled by setting a `SECONDARY_SIGN` option in the config file,
189+
which specifies the secondary signature algorithm to be used. The secondary signature
190+
option requires the option `WOLFBOOT_UNIVERSAL_KEYSTORE=1`, to ensure that the
191+
keystore can handle both classic and PQ keys.
192+
193+
The secondary signature option can be set to any of the supported PQ signature options.
194+
195+
The example configuration provided in `config/examples/sim-ml-dsa-ecc-hybrid.config`
196+
demonstrates the use of hybrid mode with both ML-DSA-65 and ECC-384.
197+
198+
### Hybrid signature
199+
200+
The `sign` tool supports hybrid signatures. It is sufficient to specify two
201+
ciphers argument from command line. When you do that, the tool expects two private
202+
key files path passed as arguments, instead of one.
203+
204+
The two public keys must be added to the same keystore. For this reason, the two
205+
keypairs must be generated either at the same time, or in two subsequent steps.
206+
207+
### Generating keypairs for hybrid signatures
208+
209+
#### Generate both keys at the same time:
210+
211+
```
212+
./tools/keytools/keygen --ml_dsa -g wolfboot_signing_private_key.der --ecc384 -g wolfboot_signing_second_private_key.der
213+
```
214+
215+
both keys are automatically added to the same keystore.
216+
217+
218+
#### Generate the two keys in separate steps
219+
220+
If you want to generate the keys in two steps, you will have to import the first
221+
key in the new keystore. The first public key is stored in `keystore.der` during
222+
the keypair generation:
223+
```
224+
./tools/keytools/keygen --ml_dsa -g wolfboot_signing_private_key.der
225+
```
226+
227+
The first 16 bytes contain the keystore header, and must be skipped:
228+
229+
```
230+
dd if=keystore.der of=ml_dsa-pubkey.der bs=1 skip=16
231+
```
232+
233+
The new keypair can be generated (`-g`) while importing (`-i`)the old public key:
234+
235+
```
236+
./tools/keytools/keygen --ml_dsa -i ml_dsa-pubkey.der -g --ecc384 wolfboot_signing_second_private_key.der
237+
```
238+
239+
The keystore generated is now ready to be used by wolfBoot for hybrid signature verification.
240+
241+
### Hybrid signature of the firmware
242+
243+
In both examples above, the two private keys are now available in separate .der files.
244+
The `sign` tool can now be used to sign the firmware with both keys:
245+
246+
```
247+
./tools/sign/sign --ml_dsa --ecc384 --sha256 test-app/image.elf wolfboot_signing_private_key.der wolfboot_signing_second_private_key.der 1
248+
```
249+
250+
The command should confirm that both keys are loaded, and that the image is signed and includes the hybrid signature:
251+
```
252+
wolfBoot KeyTools (Compiled C version)
253+
wolfBoot version 2020000
254+
Parsing arguments in hybrid mode
255+
Secondary private key: wolfboot_signing_second_private_key.der
256+
Secondary cipher: ECC384
257+
Version: 1
258+
Update type: Firmware
259+
Input image: test-app/image.elf
260+
Selected cipher: ML-DSA
261+
Selected hash : SHA256
262+
Private key: wolfboot_signing_private_key.der
263+
Secondary cipher: ECC384
264+
Secondary private key: wolfboot_signing_second_private_key.der
265+
Output image: test-app/image_v1_signed.bin
266+
Target partition id : 1
267+
info: using ML-DSA parameters: 3
268+
info: ML-DSA signature size: 3309
269+
Key buffer size: 5984
270+
info: ml-dsa priv len: 4032
271+
info: ml-dsa pub len: 1952
272+
Found ml-dsa key
273+
image header size overridden by config value (8192 bytes)
274+
Loading secondary key
275+
Key buffer size: 144
276+
Secondary ECC key, size: 96
277+
image header size overridden by config value (8192 bytes)
278+
Creating hybrid signature
279+
[...]
280+
```
281+
282+
The resulting image `image_v1_signed.bin` contains both signatures, and can be verified using a wolfBoot with hybrid signature support.
283+
181284
## Building the external PQ Integrations
182285

183286
### ext_LMS Support

hal/sim.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
#include "wolfboot/wolfboot.h"
4242
#include "target.h"
43+
#include "printf.h"
4344

4445
/* Global pointer to the internal and external flash base */
4546
uint8_t *sim_ram_base;
@@ -72,7 +73,7 @@ static int mmap_file(const char *path, uint8_t *address, uint8_t** ret_address)
7273

7374
fd = open(path, O_RDWR);
7475
if (fd == -1) {
75-
fprintf(stderr, "can't open %s\n", path);
76+
wolfBoot_printf( "can't open %s\n", path);
7677
return -1;
7778
}
7879

@@ -81,7 +82,7 @@ static int mmap_file(const char *path, uint8_t *address, uint8_t** ret_address)
8182
if (mmaped_addr == MAP_FAILED)
8283
return -1;
8384

84-
fprintf(stderr, "Simulator assigned %s to base %p\n", path, mmaped_addr);
85+
wolfBoot_printf( "Simulator assigned %s to base %p\n", path, mmaped_addr);
8586

8687
*ret_address = mmaped_addr;
8788

@@ -119,8 +120,8 @@ int hal_flash_write(uintptr_t address, const uint8_t *data, int len)
119120
uint8_t *addr = (uint8_t *)address;
120121
if (addr[i] != FLASH_BYTE_ERASED) {
121122
/* no writing to non-erased page in NVM_FLASH_WRITEONCE */
122-
printf("NVM_FLASH_WRITEONCE non-erased write detected at address %p!\n", addr);
123-
printf("Address[%d] = %02x\n", i, addr[i]);
123+
wolfBoot_printf("NVM_FLASH_WRITEONCE non-erased write detected at address %p!\n", addr);
124+
wolfBoot_printf("Address[%d] = %02x\n", i, addr[i]);
124125
return -1;
125126
}
126127
#endif
@@ -137,9 +138,9 @@ int hal_flash_write(uintptr_t address, const uint8_t *data, int len)
137138
int hal_flash_erase(uintptr_t address, int len)
138139
{
139140
/* implicit cast abide compiler warning */
140-
fprintf(stderr, "hal_flash_erase addr %p len %d\n", (void*)address, len);
141+
wolfBoot_printf( "hal_flash_erase addr %p len %d\n", (void*)address, len);
141142
if (address == erasefail_address + WOLFBOOT_PARTITION_BOOT_ADDRESS) {
142-
fprintf(stderr, "POWER FAILURE\n");
143+
wolfBoot_printf( "POWER FAILURE\n");
143144
/* Corrupt page */
144145
memset((void*)address, 0xEE, len);
145146
exit(0);
@@ -156,23 +157,23 @@ void hal_init(void)
156157
ret = mmap_file(INTERNAL_FLASH_FILE,
157158
(uint8_t*)ARCH_FLASH_OFFSET, &sim_ram_base);
158159
if (ret != 0) {
159-
fprintf(stderr, "failed to load internal flash file\n");
160+
wolfBoot_printf( "failed to load internal flash file\n");
160161
exit(-1);
161162
}
162163

163164
#ifdef EXT_FLASH
164165
ret = mmap_file(EXTERNAL_FLASH_FILE,
165166
(uint8_t*)ARCH_FLASH_OFFSET + 0x10000000, &flash_base);
166167
if (ret != 0) {
167-
fprintf(stderr, "failed to load external flash file\n");
168+
wolfBoot_printf( "failed to load external flash file\n");
168169
exit(-1);
169170
}
170171
#endif /* EXT_FLASH */
171172

172173
for (i = 1; i < main_argc; i++) {
173174
if (strcmp(main_argv[i], "powerfail") == 0) {
174175
erasefail_address = strtol(main_argv[++i], NULL, 16);
175-
fprintf(stderr, "Set power fail to erase at address %x\n",
176+
wolfBoot_printf( "Set power fail to erase at address %x\n",
176177
erasefail_address);
177178
}
178179
/* force a bad write of the boot partition to trigger and test the
@@ -262,15 +263,15 @@ void do_boot(const uint32_t *app_offset)
262263

263264
ret = NSCreateObjectFileImageFromMemory(app_buf, app_size, &fileImage);
264265
if (ret != 1 || fileImage == NULL) {
265-
fprintf(stderr, "Error loading object memory!\n");
266+
wolfBoot_printf( "Error loading object memory!\n");
266267
exit(-1);
267268
}
268269
module = NSLinkModule(fileImage, "module",
269270
(NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_BINDNOW));
270271
symbol = NSLookupSymbolInModule(module, "__mh_execute_header");
271272
pSymbolAddress = NSAddressOfSymbol(symbol);
272273
if (!find_epc(pSymbolAddress, &epc)) {
273-
fprintf(stderr, "Error finding entry point!\n");
274+
wolfBoot_printf( "Error finding entry point!\n");
274275
exit(-1);
275276
}
276277

@@ -283,17 +284,17 @@ void do_boot(const uint32_t *app_offset)
283284
char *envp[1] = {NULL};
284285
int fd = memfd_create("test_app", 0);
285286
if (fd == -1) {
286-
fprintf(stderr, "memfd error\n");
287+
wolfBoot_printf( "memfd error\n");
287288
exit(-1);
288289
}
289290

290291
if ((size_t)write(fd, app_offset, app_size) != app_size) {
291-
fprintf(stderr, "can't write test-app to memfd\n");
292+
wolfBoot_printf( "can't write test-app to memfd\n");
292293
exit(-1);
293294
}
294295

295296
ret = fexecve(fd, main_argv, envp);
296-
fprintf(stderr, "fexecve error\n");
297+
wolfBoot_printf( "fexecve error\n");
297298
#endif
298299
exit(1);
299300
}

0 commit comments

Comments
 (0)