From 213315507c6ae50c8b20c503ef912963fac064a8 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 9 Jul 2024 08:42:33 +0200 Subject: [PATCH 1/8] Add command line tool to generate otp.bin --- .gitignore | 3 + Makefile | 3 + include/otp_keystore.h | 2 - tools/keytools/otp/Makefile | 6 ++ tools/keytools/otp/otp-keystore-gen.c | 89 +++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 tools/keytools/otp/otp-keystore-gen.c diff --git a/.gitignore b/.gitignore index bd0d75afb..9262b9992 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,9 @@ tools/keytools/otp/otp-keystore-primer tools/delta/bmdiff tools/delta/bmpatch +# otp-keystore-gen binary +tools/keytools/otp/otp-keystore-gen + # Vim swap files .*.swp diff --git a/Makefile b/Makefile index cafe45eb4..247c43e0b 100644 --- a/Makefile +++ b/Makefile @@ -377,6 +377,9 @@ cppcheck: otp: tools/keytools/otp/otp-keystore-primer.bin FORCE +otpgen: + make -C tools/keytools/otp otp-keystore-gen + tools/keytools/otp/otp-keystore-primer.bin: FORCE make -C tools/keytools/otp clean make -C tools/keytools/otp diff --git a/include/otp_keystore.h b/include/otp_keystore.h index bf2fadead..1a5a640ec 100644 --- a/include/otp_keystore.h +++ b/include/otp_keystore.h @@ -34,8 +34,6 @@ #include "hal/stm32h7.h" #elif defined TARGET_stm32h5 #include "hal/stm32h5.h" -#else - #error "Unsupported target for OTP" #endif #include "keystore.h" diff --git a/tools/keytools/otp/Makefile b/tools/keytools/otp/Makefile index 363d56fd1..8d09b9ae5 100644 --- a/tools/keytools/otp/Makefile +++ b/tools/keytools/otp/Makefile @@ -32,6 +32,12 @@ CC=$(CROSS_COMPILE)gcc OBJCOPY?=$(CROSS_COMPILE)objcopy SIZE?=$(CROSS_COMPILE)size +all: otp-keystore-primer.bin otp-keystore-gen + +otp-keystore-gen: otp-keystore-gen.c ../../../src/keystore.c + gcc -o $@ $^ -I. -I../../../ -I../../../include -DFLASH_OTP_KEYSTORE + + otp-keystore-primer.bin: otp-keystore-primer.elf $(Q)$(OBJCOPY) -O binary $(^) $(@) diff --git a/tools/keytools/otp/otp-keystore-gen.c b/tools/keytools/otp/otp-keystore-gen.c new file mode 100644 index 000000000..a08bce78b --- /dev/null +++ b/tools/keytools/otp/otp-keystore-gen.c @@ -0,0 +1,89 @@ +/* otp-keystore-primer.c + * + * Command line utility to create a OTP image + * + * + * Copyright (C) 2024 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define OTP_SIZE 4096 + +#include "wolfboot/wolfboot.h" +#include "keystore.h" +#include "otp_keystore.h" + +extern struct keystore_slot PubKeys[]; + +const char outfile[] = "otp.bin"; + +int main(void) +{ + int n_keys = keystore_num_pubkeys(); + int i; + struct wolfBoot_otp_hdr hdr; + uint32_t tot_len; + int ofd; + int slot_size; + + memcpy(hdr.keystore_hdr_magic, KEYSTORE_HDR_MAGIC, 8); + hdr.item_count = n_keys; + hdr.flags = 0; + hdr.version = WOLFBOOT_VERSION; + + /* Sanity check to avoid writing an empty keystore */ + if (n_keys < 1) { + fprintf(stderr, "Error: too few keys (%d), refusing to create %s\n", n_keys, outfile); + exit(1); + } + + slot_size = keystore_get_size(0); + slot_size += KEYSTORE_HDR_SIZE; + fprintf(stderr, "Slot size: %d\n", slot_size); + + ofd = open(outfile, O_WRONLY|O_CREAT|O_TRUNC, 0600); + if (ofd < 0) { + perror("opening output file"); + exit(2); + } + + /* Write the header to the beginning of the OTP binary file */ + if (write(ofd, &hdr, sizeof(hdr)) != sizeof(hdr)) { + fprintf(stderr, "Error writing to %s: %s\n", outfile, strerror(errno)); + } + + for (i = 0; i < n_keys; i++) { + /* Write each public key to its slot in OTP */ + if (write(ofd, &PubKeys[i], + slot_size) < 0) { + fprintf(stderr, "Error adding key %d to %s: %s\n", i, outfile, strerror(errno)); + exit(3); + } + } + fprintf(stderr, "%s successfully created.\nGoodbye.\n", outfile); + close(ofd); + return 0; +} From 2082bd003c60c1b6584086084827ac7bc08817e7 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 9 Jul 2024 09:28:40 +0200 Subject: [PATCH 2/8] Avoid to silently overwrite keystore.c --- Makefile | 1 + tools/keytools/keygen.c | 18 +++++++++++++++++- tools/keytools/otp/Makefile | 4 ++-- tools/keytools/otp/otp-keystore-gen.c | 2 ++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 247c43e0b..dfb1d5a4e 100644 --- a/Makefile +++ b/Makefile @@ -302,6 +302,7 @@ clean: $(Q)rm -f wolfboot.bin wolfboot.elf wolfboot.map test-update.rom wolfboot.hex $(Q)rm -f $(MACHINE_OBJ) $(MAIN_TARGET) $(LSCRIPT) $(Q)rm -f $(OBJS) + $(Q)rm -f tools/keytools/otp/otp-keystore-gen $(Q)$(MAKE) -C test-app -s clean $(Q)$(MAKE) -C tools/check_config -s clean $(Q)$(MAKE) -C stage1 -s clean diff --git a/tools/keytools/keygen.c b/tools/keytools/keygen.c index 817f0c65b..cc4b72397 100644 --- a/tools/keytools/keygen.c +++ b/tools/keytools/keygen.c @@ -764,7 +764,7 @@ static void key_gen_check(const char *kfilename) if (!force && (f != NULL)) { char reply[40]; int replySz; - printf("** Warning: key file already exist! Are you sure you want to generate a new key and overwrite the existing key? [Type 'Yes']: "); + printf("** Warning: key file already exists! Are you sure you want to generate a new key and overwrite the existing key? [Type 'Yes']: "); fflush(stdout); replySz = scanf("%s", reply); printf("Reply is [%s]\n", reply); @@ -776,6 +776,22 @@ static void key_gen_check(const char *kfilename) unlink(kfilename); } } + f = fopen(pubkeyfile, "rb"); + if (!force && (f != NULL)) { + char reply[40]; + int replySz; + printf("** Warning: keystore already exists! Are you sure you want to generate a new key and overwrite the existing key? [Type 'Yes']: "); + fflush(stdout); + replySz = scanf("%s", reply); + printf("Reply is [%s]\n", reply); + fclose(f); + if (replySz < 0 || strcmp(reply, "Yes") != 0) { + printf("Operation aborted by user."); + exit(5); + } else { + unlink(pubkeyfile); + } + } } static void key_generate(uint32_t ktype, const char *kfilename, uint32_t id_mask) diff --git a/tools/keytools/otp/Makefile b/tools/keytools/otp/Makefile index 8d09b9ae5..6b89980b3 100644 --- a/tools/keytools/otp/Makefile +++ b/tools/keytools/otp/Makefile @@ -34,8 +34,8 @@ SIZE?=$(CROSS_COMPILE)size all: otp-keystore-primer.bin otp-keystore-gen -otp-keystore-gen: otp-keystore-gen.c ../../../src/keystore.c - gcc -o $@ $^ -I. -I../../../ -I../../../include -DFLASH_OTP_KEYSTORE +otp-keystore-gen: otp-keystore-gen.c + gcc -o $@ otp-keystore-gen.c ../../../src/keystore.c -I. -I../../../ -I../../../include -DFLASH_OTP_KEYSTORE otp-keystore-primer.bin: otp-keystore-primer.elf diff --git a/tools/keytools/otp/otp-keystore-gen.c b/tools/keytools/otp/otp-keystore-gen.c index a08bce78b..afbe43298 100644 --- a/tools/keytools/otp/otp-keystore-gen.c +++ b/tools/keytools/otp/otp-keystore-gen.c @@ -63,6 +63,8 @@ int main(void) slot_size = keystore_get_size(0); slot_size += KEYSTORE_HDR_SIZE; fprintf(stderr, "Slot size: %d\n", slot_size); + fprintf(stderr, "Number of slots: %d\n", n_keys); + fprintf(stderr, "%s size: %d\n", outfile, slot_size * n_keys + sizeof(struct wolfBoot_otp_hdr)); ofd = open(outfile, O_WRONLY|O_CREAT|O_TRUNC, 0600); if (ofd < 0) { From b59f11bc88bb760a6ae44a0072a82b58e4efef8e Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 9 Jul 2024 09:35:14 +0200 Subject: [PATCH 3/8] Integrated documentation for otpgen --- docs/flash-OTP.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/flash-OTP.md b/docs/flash-OTP.md index 140012319..e3d994176 100644 --- a/docs/flash-OTP.md +++ b/docs/flash-OTP.md @@ -20,9 +20,26 @@ The public keys are stored in the OTP area, after an initial 16-byte header that keys stored, the size of each key, and other information. In order for wolfBoot to start authenticating the firmware images at boot and upon update, the public keys -must be provisioned to the OTP area in a separate step, as described in the next section. +must be provisioned to the OTP area in a separate step, as described in the next sections. -### Provisioning the public keys to the OTP area +Depending on the target device, you can either prepare a binary image of the OTP area content, or use `otp-keystore-primer` firmware to directly provision the keys on the target. + +### Creating an image of the OTP area content + +It is possible to create a binary image of the content for the OTP area. The resulting file (otp.bin) can be manually provisioned using any external tool that allows writing to the target OTP area. + +To compile the otp-keystore-gen tool using the current keystore content: + +``` +make otpgen +``` + +And then, to create the image file `otp.bin`: + +tools/keytools/otp/otp-keystore-gen + + +### Directly provisioning the public keys to the OTP area (primer) After enabling the `FLASH_OTP_KEYSTORE` option in your `.config` file, when you compile wolfBoot by running "make", an additional application called `otp-keystore-primer` is generated under `tools/keytools/otp`. This application is used to From 398c6608277374c8381b64f48046785321581ba3 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 9 Jul 2024 15:51:43 +0200 Subject: [PATCH 4/8] External key test: cleanup keystore.c before importing --- .github/workflows/test-keytools.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-keytools.yml b/.github/workflows/test-keytools.yml index dc64b5a49..7368c9e8c 100644 --- a/.github/workflows/test-keytools.yml +++ b/.github/workflows/test-keytools.yml @@ -33,6 +33,10 @@ jobs: run: | make SIGN=ECC256 HASH=SHA256 + - name: Remove default keystore + run: | + rm -f src/keystore.c + - name: Generate external key run: | openssl ecparam -name prime256v1 -genkey -noout -outform DER -out private-key.der @@ -75,6 +79,10 @@ jobs: run: | make SIGN=ED25519 HASH=SHA256 + - name: Remove default keystore + run: | + rm -f src/keystore.c + - name: Generate external key run: | openssl genpkey -algorithm ed25519 -out private-key.der -outform DER @@ -117,6 +125,10 @@ jobs: run: | make SIGN=RSA2048 HASH=SHA256 + - name: Remove default keystore + run: | + rm -f src/keystore.c + - name: Generate external key run: | openssl genrsa -out private-key.pem 2048 @@ -258,7 +270,7 @@ jobs: run: | ./tools/keytools/sign --ecc256 --sha256 --custom-tlv 0x45 4 0x6f616943 test-app/image.elf wolfboot_signing_private_key.der 2 grep "Ciao" test-app/image_v2_signed.bin - + - name: Sign app with custom buffer TLV included run: | ./tools/keytools/sign --ecc256 --sha256 --custom-tlv-buffer 0x46 48656C6C6F20776F726C64 test-app/image.elf wolfboot_signing_private_key.der 3 @@ -268,5 +280,5 @@ jobs: run: | ./tools/keytools/sign --ecc256 --sha256 --custom-tlv-string 0x46 "Hello world" test-app/image.elf wolfboot_signing_private_key.der 3 grep "Hello world" test-app/image_v3_signed.bin - + From 8f41132ca38f72e3ae5aa8d8bf723f077f6e2a81 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 9 Jul 2024 16:31:18 +0200 Subject: [PATCH 5/8] Added more verbosity to key import failure error --- tools/keytools/keygen.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/keytools/keygen.c b/tools/keytools/keygen.c index cc4b72397..866809dbf 100644 --- a/tools/keytools/keygen.c +++ b/tools/keytools/keygen.c @@ -900,9 +900,13 @@ static void key_import(uint32_t ktype, const char *fname, uint32_t id_mask) else if (ktype == KEYGEN_ED25519) { initKey = ret = wc_Ed25519PublicKeyDecode(buf, &keySzOut, ed25519Key, readLen); + if (ret < 0) + printf("error: wc_Ed25519PublicKeyDecode failed on %s\n", fname); if (ret == 0) ret = wc_ed25519_export_public(ed25519Key, buf, &qxSz); + if (ret < 0) + printf("error: wc_ed25519_export_public failed on %s\n", fname); if (initKey == 0) wc_ed25519_free(ed25519Key); From 46f0d879462367c276553c13d5bf23f2fdccaca7 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 9 Jul 2024 17:42:58 +0200 Subject: [PATCH 6/8] test: revert keystore.c overwrite check --- tools/keytools/keygen.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/keytools/keygen.c b/tools/keytools/keygen.c index 866809dbf..48d8f009a 100644 --- a/tools/keytools/keygen.c +++ b/tools/keytools/keygen.c @@ -776,6 +776,7 @@ static void key_gen_check(const char *kfilename) unlink(kfilename); } } +#if 0 f = fopen(pubkeyfile, "rb"); if (!force && (f != NULL)) { char reply[40]; @@ -792,6 +793,7 @@ static void key_gen_check(const char *kfilename) unlink(pubkeyfile); } } +#endif } static void key_generate(uint32_t ktype, const char *kfilename, uint32_t id_mask) From 4f5e168350dcc6fecee5fe40d69e1db9a9184789 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 9 Jul 2024 18:03:51 +0200 Subject: [PATCH 7/8] Moved keystore.c overwrite check --- tools/keytools/keygen.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/tools/keytools/keygen.c b/tools/keytools/keygen.c index 48d8f009a..8b4234b12 100644 --- a/tools/keytools/keygen.c +++ b/tools/keytools/keygen.c @@ -776,24 +776,6 @@ static void key_gen_check(const char *kfilename) unlink(kfilename); } } -#if 0 - f = fopen(pubkeyfile, "rb"); - if (!force && (f != NULL)) { - char reply[40]; - int replySz; - printf("** Warning: keystore already exists! Are you sure you want to generate a new key and overwrite the existing key? [Type 'Yes']: "); - fflush(stdout); - replySz = scanf("%s", reply); - printf("Reply is [%s]\n", reply); - fclose(f); - if (replySz < 0 || strcmp(reply, "Yes") != 0) { - printf("Operation aborted by user."); - exit(5); - } else { - unlink(pubkeyfile); - } - } -#endif } static void key_generate(uint32_t ktype, const char *kfilename, uint32_t id_mask) @@ -1060,6 +1042,23 @@ int main(int argc, char** argv) printf("Keytype: %s\n", KName[keytype]); if (keytype == 0) exit(0); + fpub = fopen(pubkeyfile, "rb"); + if (!force && (fpub != NULL)) { + char reply[40]; + int replySz; + printf("** Warning: keystore already exists! Are you sure you want to generate a new key and overwrite the existing key? [Type 'Yes']: "); + fflush(stdout); + replySz = scanf("%s", reply); + printf("Reply is [%s]\n", reply); + fclose(fpub); + if (replySz < 0 || strcmp(reply, "Yes") != 0) { + printf("Operation aborted by user."); + exit(5); + } else { + unlink(pubkeyfile); + } + fpub = NULL; + } fpub = fopen(pubkeyfile, "w"); if (fpub == NULL) { fprintf(stderr, "Unable to open file '%s' for writing: %s", pubkeyfile, strerror(errno)); From c6586f2d859b2bca824510da7f616429b9ee86b4 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 9 Jul 2024 18:59:17 +0200 Subject: [PATCH 8/8] test: Delete keystore after removing private key --- .github/workflows/test-keytools.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/test-keytools.yml b/.github/workflows/test-keytools.yml index 7368c9e8c..b488ff57a 100644 --- a/.github/workflows/test-keytools.yml +++ b/.github/workflows/test-keytools.yml @@ -236,6 +236,10 @@ jobs: - name: Delete generated key run: | rm -f wolfboot_signing_private_key.der + + - name: Remove generated keystore + run: | + rm -f src/keystore.c - name: Run keygen with --id 0 run: | @@ -245,6 +249,10 @@ jobs: run: | rm -f wolfboot_signing_private_key.der + - name: Remove generated keystore + run: | + rm -f src/keystore.c + - name: Run keygen with test id set run: | ./tools/keytools/keygen --id 1,3,5,10,11,13,14 --ecc256 -g wolfboot_signing_private_key.der | grep "mask" | grep "00006c2a"