diff --git a/.github/workflows/test-wolfhsm-simulator.yml b/.github/workflows/test-wolfhsm-simulator.yml index a5646d1ac1..c4ff84deb3 100644 --- a/.github/workflows/test-wolfhsm-simulator.yml +++ b/.github/workflows/test-wolfhsm-simulator.yml @@ -44,6 +44,28 @@ jobs: needs_posix_server: false posix_server_nvminit: false needs_nvm_image: true + - name: "wolfHSM client cert chain verify mixed (ECC256 chain, RSA4096 leaf)" + file: "config/examples/sim-wolfHSM-client-certchain-mixed.config" + needs_posix_server: true + posix_server_nvminit: true + needs_nvm_image: false + - name: "wolfHSM server cert chain verify mixed (ECC256 chain, RSA4096 leaf)" + file: "config/examples/sim-wolfHSM-server-certchain-mixed.config" + needs_posix_server: false + posix_server_nvminit: false + needs_nvm_image: true + - name: "wolfHSM client cert chain verify mixed inverse (RSA4096 chain, ECC256 leaf)" + file: "config/examples/sim-wolfHSM-client-certchain-mixed.config" + needs_posix_server: true + posix_server_nvminit: true + needs_nvm_image: false + make_overrides: "SIGN=ECC256 HASH=SHA256 CERT_CHAIN_GEN_CA_ALGO=rsa4096" + - name: "wolfHSM server cert chain verify mixed inverse (RSA4096 chain, ECC256 leaf)" + file: "config/examples/sim-wolfHSM-server-certchain-mixed.config" + needs_posix_server: false + posix_server_nvminit: false + needs_nvm_image: true + make_overrides: "SIGN=ECC256 HASH=SHA256 CERT_CHAIN_GEN_CA_ALGO=rsa4096" fail-fast: false @@ -74,7 +96,7 @@ jobs: - name: Build wolfboot.elf run: | - make clean && make test-sim-internal-flash-with-update + make clean && make ${{ matrix.config.make_overrides }} test-sim-internal-flash-with-update - name: Build example POSIX TCP server if: matrix.config.needs_posix_server diff --git a/Makefile b/Makefile index 2986d8db51..058d2725c8 100644 --- a/Makefile +++ b/Makefile @@ -378,7 +378,7 @@ ifeq ($(USER_PRIVATE_KEY),) $(Q)(test $(SIGN) = NONE) || ($(SIGN_ENV) "$(KEYGEN_TOOL)" $(KEYGEN_OPTIONS) -g wolfboot_signing_private_key.der) || true $(Q)(test $(SIGN) = NONE) && (echo "// SIGN=NONE" > src/keystore.c) || true $(Q)(test "$(FLASH_OTP_KEYSTORE)" = "1") && (make -C tools/keytools/otp) || true - $(Q)(test $(SIGN) = NONE) || (test "$(CERT_CHAIN_VERIFY)" = "") || (test "$(USER_CERT_CHAIN)" != "") || (tools/scripts/sim-gen-dummy-chain.sh --algo $(CERT_CHAIN_GEN_ALGO) --leaf wolfboot_signing_private_key.der) + $(Q)(test $(SIGN) = NONE) || (test "$(CERT_CHAIN_VERIFY)" = "") || (test "$(USER_CERT_CHAIN)" != "") || (tools/scripts/sim-gen-dummy-chain.sh $(CERT_CHAIN_GEN_FLAGS) --leaf wolfboot_signing_private_key.der) else @echo "Using user-provided private key: $(USER_PRIVATE_KEY)" endif diff --git a/config/examples/sim-wolfHSM-client-certchain-mixed.config b/config/examples/sim-wolfHSM-client-certchain-mixed.config new file mode 100644 index 0000000000..31e05c33e6 --- /dev/null +++ b/config/examples/sim-wolfHSM-client-certchain-mixed.config @@ -0,0 +1,36 @@ +ARCH=sim +TARGET=sim +SIGN?=RSA4096 +HASH?=SHA384 +WOLFBOOT_SMALL_STACK?=0 +SPI_FLASH=0 +DEBUG=0 +SPMATH=1 + +# RSA4096 in the chain or image signature path needs the larger stack and header +WOLFBOOT_HUGE_STACK=1 +IMAGE_HEADER_SIZE=4096 + +# Cert chain options (defaults — overridable by the build environment): +# - CA chain (root + intermediate) signed with ECC256 + SHA256 +# - Leaf cert identity is RSA4096 (fixed by SIGN, signs the image with SHA384) +# - AUX_PK_ALGOS / AUX_HASH_ALGOS are auto-populated from CERT_CHAIN_GEN_* +CERT_CHAIN_VERIFY=1 +CERT_CHAIN_GEN_CA_ALGO?=ecc256 +CERT_CHAIN_GEN_CA_HASH?=sha256 + +# wolfHSM options +WOLFHSM_CLIENT=1 + +# sizes should be multiple of system page size +WOLFBOOT_PARTITION_SIZE=0x200000 +WOLFBOOT_SECTOR_SIZE=0x1000 +WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x280000 +WOLFBOOT_PARTITION_SWAP_ADDRESS=0x480000 + +# required for keytools +WOLFBOOT_FIXED_PARTITIONS=1 + +# For debugging XMALLOC/XFREE +#CFLAGS_EXTRA+=-DWOLFBOOT_DEBUG_MALLOC diff --git a/config/examples/sim-wolfHSM-server-certchain-mixed.config b/config/examples/sim-wolfHSM-server-certchain-mixed.config new file mode 100644 index 0000000000..5d928fdaed --- /dev/null +++ b/config/examples/sim-wolfHSM-server-certchain-mixed.config @@ -0,0 +1,36 @@ +ARCH=sim +TARGET=sim +SIGN?=RSA4096 +HASH?=SHA384 +WOLFBOOT_SMALL_STACK?=0 +SPI_FLASH=0 +DEBUG=0 +SPMATH=1 + +# RSA4096 in the chain or image signature path needs the larger stack and header +WOLFBOOT_HUGE_STACK=1 +IMAGE_HEADER_SIZE=4096 + +# Cert chain options (defaults — overridable by the build environment): +# - CA chain (root + intermediate) signed with ECC256 + SHA256 +# - Leaf cert identity is RSA4096 (fixed by SIGN, signs the image with SHA384) +# - AUX_PK_ALGOS / AUX_HASH_ALGOS are auto-populated from CERT_CHAIN_GEN_* +CERT_CHAIN_VERIFY=1 +CERT_CHAIN_GEN_CA_ALGO?=ecc256 +CERT_CHAIN_GEN_CA_HASH?=sha256 + +# wolfHSM options +WOLFHSM_SERVER=1 + +# sizes should be multiple of system page size +WOLFBOOT_PARTITION_SIZE=0x200000 +WOLFBOOT_SECTOR_SIZE=0x1000 +WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x280000 +WOLFBOOT_PARTITION_SWAP_ADDRESS=0x480000 + +# required for keytools +WOLFBOOT_FIXED_PARTITIONS=1 + +# For debugging XMALLOC/XFREE +#CFLAGS_EXTRA+=-DWOLFBOOT_DEBUG_MALLOC diff --git a/include/image.h b/include/image.h index 69b8dc83e1..808e4787f8 100644 --- a/include/image.h +++ b/include/image.h @@ -66,12 +66,11 @@ extern "C" { defined (WOLFBOOT_SIGN_RSA2048ENC) || \ defined (WOLFBOOT_SIGN_RSA3072ENC) || \ defined (WOLFBOOT_SIGN_RSA4096ENC) -#define wolfBoot_verify_signature_primary wolfBoot_verify_signature_rsa -#endif -#if defined (WOLFBOOT_SIGN_RSAPSS2048) || \ - defined (WOLFBOOT_SIGN_RSAPSS3072) || \ - defined (WOLFBOOT_SIGN_RSAPSS4096) -#define wolfBoot_verify_signature_primary wolfBoot_verify_signature_rsa_pss +# ifdef WOLFBOOT_SIGN_RSA_PSS +# define wolfBoot_verify_signature_primary wolfBoot_verify_signature_rsa_pss +# else +# define wolfBoot_verify_signature_primary wolfBoot_verify_signature_rsa +# endif #endif #if defined (WOLFBOOT_SIGN_ECC256) || \ defined (WOLFBOOT_SIGN_ECC384) || \ @@ -100,12 +99,11 @@ extern "C" { defined (WOLFBOOT_SIGN_SECONDARY_RSA2048ENC) || \ defined (WOLFBOOT_SIGN_SECONDARY_RSA3072ENC) || \ defined (WOLFBOOT_SIGN_SECONDARY_RSA4096ENC) -#define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_rsa -#endif -#if defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS2048) || \ - defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) || \ - defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS4096) -#define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_rsa_pss +# ifdef WOLFBOOT_SIGN_SECONDARY_RSA_PSS +# define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_rsa_pss +# else +# define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_rsa +# endif #endif #if defined (WOLFBOOT_SIGN_SECONDARY_ECC256) || \ defined (WOLFBOOT_SIGN_SECONDARY_ECC384) || \ diff --git a/include/loader.h b/include/loader.h index 930a7d410d..1e45c41ba8 100644 --- a/include/loader.h +++ b/include/loader.h @@ -40,41 +40,29 @@ extern "C" { #define ECC_IMAGE_SIGNATURE_SIZE (132) #endif -/* Consolidated RSA-PSS flag: set when any RSA-PSS variant is enabled - * (primary or secondary). Used to guard PSS-specific code paths in the - * unified RSA verify function. */ -#if defined(WOLFBOOT_SIGN_RSAPSS2048) || defined(WOLFBOOT_SIGN_RSAPSS3072) || \ - defined(WOLFBOOT_SIGN_RSAPSS4096) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS2048) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS4096) +/* Consolidated RSA-PSS flag: set when either firmware (primary or secondary) + * uses RSA-PSS padding. Used to guard PSS-specific code paths in the unified + * RSA verify function. PSS is encoded as a modifier orthogonal to size, so + * any RSA size combined with the PSS modifier feeds this aggregate. */ +#if defined(WOLFBOOT_SIGN_RSA_PSS) || defined(WOLFBOOT_SIGN_SECONDARY_RSA_PSS) #define WOLFBOOT_SIGN_RSAPSS_ANY #endif -/* Consolidated RSA flag: set when any RSA variant is enabled (PKCS#1 v1.5 or - * PSS, primary or secondary). Used to guard the unified RSA verify function - * and related RSA code paths. */ +/* Consolidated RSA flag: set when any RSA size is enabled (primary or + * secondary). Padding mode is independent — see WOLFBOOT_SIGN_RSAPSS_ANY. */ #if defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_RSA3072) || \ defined(WOLFBOOT_SIGN_RSA4096) || \ defined(WOLFBOOT_SIGN_SECONDARY_RSA2048) || \ defined(WOLFBOOT_SIGN_SECONDARY_RSA3072) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSA4096) || \ - defined(WOLFBOOT_SIGN_RSAPSS2048) || defined(WOLFBOOT_SIGN_RSAPSS3072) || \ - defined(WOLFBOOT_SIGN_RSAPSS4096) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS2048) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS4096) + defined(WOLFBOOT_SIGN_SECONDARY_RSA4096) #define WOLFBOOT_SIGN_RSA_ANY #endif -#if defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_SECONDARY_RSA2048) || \ - defined(WOLFBOOT_SIGN_RSAPSS2048) || defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS2048) +#if defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_SECONDARY_RSA2048) #define RSA_IMAGE_SIGNATURE_SIZE (256) -#elif defined(WOLFBOOT_SIGN_RSA3072) || defined(WOLFBOOT_SIGN_SECONDARY_RSA3072) || \ - defined(WOLFBOOT_SIGN_RSAPSS3072) || defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) +#elif defined(WOLFBOOT_SIGN_RSA3072) || defined(WOLFBOOT_SIGN_SECONDARY_RSA3072) #define RSA_IMAGE_SIGNATURE_SIZE (384) -#elif defined(WOLFBOOT_SIGN_RSA4096) || defined(WOLFBOOT_SIGN_SECONDARY_RSA4096) || \ - defined(WOLFBOOT_SIGN_RSAPSS4096) || defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS4096) +#elif defined(WOLFBOOT_SIGN_RSA4096) || defined(WOLFBOOT_SIGN_SECONDARY_RSA4096) #define RSA_IMAGE_SIGNATURE_SIZE (512) #endif diff --git a/include/user_settings.h b/include/user_settings.h index b4eb7150c4..9e6ab8b17b 100644 --- a/include/user_settings.h +++ b/include/user_settings.h @@ -74,7 +74,8 @@ extern int tolower(int c); #endif /* ED25519 and SHA512 */ -#if defined(WOLFBOOT_SIGN_ED25519) || defined(WOLFBOOT_SIGN_SECONDARY_ED25519) +#if defined(WOLFBOOT_SIGN_ED25519) || defined(WOLFBOOT_SIGN_SECONDARY_ED25519) || \ + defined(WOLFBOOT_AUX_PK_ED25519) # define HAVE_ED25519 # define ED25519_SMALL # if !defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) @@ -86,7 +87,8 @@ extern int tolower(int c); #endif /* ED448 and SHA3/SHAKE256 */ -#if defined(WOLFBOOT_SIGN_ED448) || defined(WOLFBOOT_SIGN_SECONDARY_ED448) +#if defined(WOLFBOOT_SIGN_ED448) || defined(WOLFBOOT_SIGN_SECONDARY_ED448) || \ + defined(WOLFBOOT_AUX_PK_ED448) # define HAVE_ED448 # define HAVE_ED448_VERIFY # define ED448_SMALL @@ -106,6 +108,9 @@ extern int tolower(int c); defined(WOLFBOOT_SIGN_SECONDARY_ECC256) || \ defined(WOLFBOOT_SIGN_SECONDARY_ECC384) || \ defined(WOLFBOOT_SIGN_SECONDARY_ECC521) || \ + defined(WOLFBOOT_AUX_PK_ECC256) || \ + defined(WOLFBOOT_AUX_PK_ECC384) || \ + defined(WOLFBOOT_AUX_PK_ECC521) || \ defined(WOLFCRYPT_SECURE_MODE) || \ defined(WOLFCRYPT_TEST) || defined(WOLFCRYPT_BENCHMARK) @@ -168,11 +173,13 @@ extern int tolower(int c); /* Curve */ # if defined(WOLFBOOT_SIGN_ECC256) || defined(WOLFCRYPT_SECURE_MODE) || \ defined(WOLFBOOT_SIGN_SECONDARY_ECC256) || \ + defined(WOLFBOOT_AUX_PK_ECC256) || \ defined(WOLFCRYPT_TEST) || defined(WOLFCRYPT_BENCHMARK) # define HAVE_ECC256 # endif # if defined(WOLFBOOT_SIGN_ECC384) || \ defined(WOLFBOOT_SIGN_SECONDARY_ECC384) || \ + defined(WOLFBOOT_AUX_PK_ECC384) || \ defined(WOLFCRYPT_SECURE_MODE) || \ defined(WOLFCRYPT_TEST) || defined(WOLFCRYPT_BENCHMARK) # define HAVE_ECC384 @@ -181,6 +188,7 @@ extern int tolower(int c); /* ECC521 only enabled if specifically requested (not for tests - too large) */ # if defined(WOLFBOOT_SIGN_ECC521) || \ defined(WOLFBOOT_SIGN_SECONDARY_ECC521) || \ + defined(WOLFBOOT_AUX_PK_ECC521) || \ defined(WOLFCRYPT_SECURE_MODE) # define HAVE_ECC521 # define WOLFSSL_SP_521 @@ -215,30 +223,28 @@ extern int tolower(int c); * WOLFBOOT_SIGN_SECONDARY_ECC256 || WOLFCRYPT_SECURE_MODE */ -/* RSA */ +/* RSA. Size and padding mode are independent: the SIGN_RSA{N} / + * SIGN_SECONDARY_RSA{N} / AUX_PK_RSA{N} macros select size; the + * SIGN_RSA_PSS / SIGN_SECONDARY_RSA_PSS / AUX_PK_RSA_PSS modifiers (gated + * separately below) add PSS padding support. */ #if defined(WOLFBOOT_SIGN_RSA2048) || \ defined(WOLFBOOT_SIGN_RSA3072) || \ defined(WOLFBOOT_SIGN_RSA4096) || \ defined(WOLFBOOT_SIGN_SECONDARY_RSA2048) || \ defined(WOLFBOOT_SIGN_SECONDARY_RSA3072) || \ defined(WOLFBOOT_SIGN_SECONDARY_RSA4096) || \ - defined(WOLFBOOT_SIGN_RSAPSS2048) || \ - defined(WOLFBOOT_SIGN_RSAPSS3072) || \ - defined(WOLFBOOT_SIGN_RSAPSS4096) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS2048) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS4096) || \ + defined(WOLFBOOT_AUX_PK_RSA2048) || \ + defined(WOLFBOOT_AUX_PK_RSA3072) || \ + defined(WOLFBOOT_AUX_PK_RSA4096) || \ (defined(WOLFCRYPT_SECURE_MODE) && (!defined(PKCS11_SMALL))) # define WC_RSA_BLINDING # define WC_RSA_DIRECT # define RSA_LOW_MEM # define WC_ASN_HASH_SHA256 -# if defined(WOLFBOOT_SIGN_RSAPSS2048) || defined(WOLFBOOT_SIGN_RSAPSS3072) || \ - defined(WOLFBOOT_SIGN_RSAPSS4096) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS2048) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) || \ - defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS4096) +# if defined(WOLFBOOT_SIGN_RSA_PSS) || \ + defined(WOLFBOOT_SIGN_SECONDARY_RSA_PSS) || \ + defined(WOLFBOOT_AUX_PK_RSA_PSS) # define WC_RSA_PSS # endif # if !defined(WOLFBOOT_TPM) && !defined(WOLFCRYPT_SECURE_MODE) && \ @@ -259,8 +265,13 @@ extern int tolower(int c); # define WOLFSSL_SP_SMALL # define WOLFSSL_SP_MATH # endif -# if defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_SECONDARY_RSA2048) || \ - defined(WOLFBOOT_SIGN_RSAPSS2048) || defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS2048) + /* If ECC was already enabled (e.g. via WOLFBOOT_AUX_PK_ECC*), the ECC + * block above will have set FP_MAX_BITS / SP_INT_BITS for the ECC curve + * size. RSA needs much wider math, so undefine first and let the + * algo-specific block below redefine. */ +# undef FP_MAX_BITS +# undef SP_INT_BITS +# if defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_SECONDARY_RSA2048) # define FP_MAX_BITS (2048 * 2) # define SP_INT_BITS 2048 # define WOLFSSL_SP_NO_3072 @@ -269,8 +280,7 @@ extern int tolower(int c); # define RSA_MIN_SIZE 2048 # define RSA_MAX_SIZE 2048 # endif -# if defined(WOLFBOOT_SIGN_RSA3072) || defined(WOLFBOOT_SIGN_SECONDARY_RSA3072) || \ - defined(WOLFBOOT_SIGN_RSAPSS3072) || defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) +# if defined(WOLFBOOT_SIGN_RSA3072) || defined(WOLFBOOT_SIGN_SECONDARY_RSA3072) # define FP_MAX_BITS (3072 * 2) # define SP_INT_BITS 3072 # define WOLFSSL_SP_NO_2048 @@ -280,8 +290,7 @@ extern int tolower(int c); # define RSA_MAX_SIZE 3072 # endif -# if defined(WOLFBOOT_SIGN_RSA4096) || defined(WOLFBOOT_SIGN_SECONDARY_RSA4096) || \ - defined(WOLFBOOT_SIGN_RSAPSS4096) || defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS4096) +# if defined(WOLFBOOT_SIGN_RSA4096) || defined(WOLFBOOT_SIGN_SECONDARY_RSA4096) # define FP_MAX_BITS (4096 * 2) # define SP_INT_BITS 4096 # define WOLFSSL_SP_NO_2048 @@ -300,6 +309,37 @@ extern int tolower(int c); # define RSA_MIN_SIZE 2048 # define RSA_MAX_SIZE 4096 # endif +# if defined(WOLFBOOT_AUX_PK_RSA2048) || \ + defined(WOLFBOOT_AUX_PK_RSA3072) || \ + defined(WOLFBOOT_AUX_PK_RSA4096) + /* Widen math constraints to support aux RSA key sizes */ +# undef FP_MAX_BITS +# undef SP_INT_BITS +# undef RSA_MIN_SIZE +# undef RSA_MAX_SIZE +# undef WOLFSSL_SP_NO_2048 +# undef WOLFSSL_SP_NO_3072 +# undef WOLFSSL_SP_NO_4096 +# define FP_MAX_BITS (4096 * 2) +# define SP_INT_BITS 4096 +# define RSA_MIN_SIZE 2048 +# define RSA_MAX_SIZE 4096 +# if defined(WOLFBOOT_SIGN_RSA2048) || \ + defined(WOLFBOOT_SIGN_SECONDARY_RSA2048) || \ + defined(WOLFBOOT_AUX_PK_RSA2048) +# define WOLFSSL_SP_2048 +# endif +# if defined(WOLFBOOT_SIGN_RSA3072) || \ + defined(WOLFBOOT_SIGN_SECONDARY_RSA3072) || \ + defined(WOLFBOOT_AUX_PK_RSA3072) +# define WOLFSSL_SP_3072 +# endif +# if defined(WOLFBOOT_SIGN_RSA4096) || \ + defined(WOLFBOOT_SIGN_SECONDARY_RSA4096) || \ + defined(WOLFBOOT_AUX_PK_RSA4096) +# define WOLFSSL_SP_4096 +# endif +# endif #else # define NO_RSA #endif /* RSA */ @@ -354,6 +394,24 @@ extern int tolower(int c); # endif #endif +/* Auxiliary hash algorithms (e.g. for cert chain verification) */ +#if defined(WOLFBOOT_AUX_HASH_SHA384) || defined(WOLFBOOT_AUX_HASH_SHA512) +# ifndef WOLFSSL_SHA384 +# define WOLFSSL_SHA384 +# endif +# ifndef WOLFSSL_SHA512 +# define WOLFSSL_SHA512 +# define WOLFSSL_NOSHA512_224 +# define WOLFSSL_NOSHA512_256 +# endif +#endif + +#ifdef WOLFBOOT_AUX_HASH_SHA3 +# ifndef WOLFSSL_SHA3 +# define WOLFSSL_SHA3 +# endif +#endif + /* If SP math is enabled determine word size */ #if defined(WOLFSSL_HAVE_SP_ECC) || defined(WOLFSSL_HAVE_SP_RSA) # ifdef __aarch64__ diff --git a/include/wolfboot/wolfboot.h b/include/wolfboot/wolfboot.h index d02355c0a7..3990c7931c 100644 --- a/include/wolfboot/wolfboot.h +++ b/include/wolfboot/wolfboot.h @@ -126,16 +126,16 @@ extern "C" { #ifndef IMAGE_HEADER_SIZE /* Largest cases first */ -# if defined(WOLFBOOT_SIGN_RSA4096) || defined(WOLFBOOT_SIGN_RSAPSS4096) +# if defined(WOLFBOOT_SIGN_RSA4096) # define IMAGE_HEADER_SIZE 1024 - /* RSA3072/RSAPSS3072 + strong hash */ -# elif ((defined(WOLFBOOT_SIGN_RSA3072) || defined(WOLFBOOT_SIGN_RSAPSS3072)) && \ + /* RSA3072 + strong hash (any padding mode) */ +# elif (defined(WOLFBOOT_SIGN_RSA3072) && \ (defined(WOLFBOOT_HASH_SHA384) || defined(WOLFBOOT_HASH_SHA3_384))) # define IMAGE_HEADER_SIZE 1024 - /* RSA2048/RSAPSS2048 + SHA256 */ -# elif (defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_RSAPSS2048)) && defined(WOLFBOOT_HASH_SHA256) + /* RSA2048 + SHA256 (any padding mode) */ +# elif defined(WOLFBOOT_SIGN_RSA2048) && defined(WOLFBOOT_HASH_SHA256) # define IMAGE_HEADER_SIZE 512 /* ECC384 requires 512 with SHA256 */ @@ -155,7 +155,7 @@ extern "C" { # define IMAGE_HEADER_SIZE 256 /* Secondary 512-byte fallbacks */ -# elif defined(WOLFBOOT_SIGN_RSA3072) || defined(WOLFBOOT_SIGN_RSAPSS3072) || \ +# elif defined(WOLFBOOT_SIGN_RSA3072) || \ defined(WOLFBOOT_SIGN_ECC521) || \ defined(WOLFBOOT_SIGN_ED448) || \ defined(WOLFBOOT_HASH_SHA384) || \ @@ -457,33 +457,34 @@ extern "C" { # ifndef WOLFBOOT_UNIVERSAL_KEYSTORE # define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_ECC521 # endif - #elif defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_RSA2048ENC) - # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_RSA2048 + /* PSS variants must come before plain RSA so the modifier wins. */ + #elif defined(WOLFBOOT_SIGN_RSA_PSS) && defined(WOLFBOOT_SIGN_RSA2048) + # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_RSAPSS2048 # ifndef WOLFBOOT_UNIVERSAL_KEYSTORE # define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_RSA2048 # endif - #elif defined(WOLFBOOT_SIGN_RSA3072) || defined(WOLFBOOT_SIGN_RSA3072ENC) - # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_RSA3072 + #elif defined(WOLFBOOT_SIGN_RSA_PSS) && defined(WOLFBOOT_SIGN_RSA3072) + # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_RSAPSS3072 # ifndef WOLFBOOT_UNIVERSAL_KEYSTORE # define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_RSA3072 # endif - #elif defined(WOLFBOOT_SIGN_RSA4096) || defined(WOLFBOOT_SIGN_RSA4096ENC) - # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_RSA4096 + #elif defined(WOLFBOOT_SIGN_RSA_PSS) && defined(WOLFBOOT_SIGN_RSA4096) + # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_RSAPSS4096 # ifndef WOLFBOOT_UNIVERSAL_KEYSTORE # define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_RSA4096 # endif - #elif defined(WOLFBOOT_SIGN_RSAPSS2048) - # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_RSAPSS2048 + #elif defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_RSA2048ENC) + # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_RSA2048 # ifndef WOLFBOOT_UNIVERSAL_KEYSTORE # define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_RSA2048 # endif - #elif defined(WOLFBOOT_SIGN_RSAPSS3072) - # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_RSAPSS3072 + #elif defined(WOLFBOOT_SIGN_RSA3072) || defined(WOLFBOOT_SIGN_RSA3072ENC) + # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_RSA3072 # ifndef WOLFBOOT_UNIVERSAL_KEYSTORE # define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_RSA3072 # endif - #elif defined(WOLFBOOT_SIGN_RSAPSS4096) - # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_RSAPSS4096 + #elif defined(WOLFBOOT_SIGN_RSA4096) || defined(WOLFBOOT_SIGN_RSA4096ENC) + # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_RSA4096 # ifndef WOLFBOOT_UNIVERSAL_KEYSTORE # define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_RSA4096 # endif diff --git a/options.mk b/options.mk index b70cb4e6b8..0a0e429b98 100644 --- a/options.mk +++ b/options.mk @@ -311,7 +311,7 @@ ifeq ($(SIGN),RSAPSS2048) SIGN_ALG=RSAPSS2048 WOLFCRYPT_OBJS+= $(RSA_OBJS) WOLFCRYPT_OBJS+=$(MATH_OBJS) - CFLAGS+=-D"WOLFBOOT_SIGN_RSAPSS2048" $(RSA_EXTRA_CFLAGS) + CFLAGS+=-D"WOLFBOOT_SIGN_RSA2048" -DWOLFBOOT_SIGN_RSA_PSS $(RSA_EXTRA_CFLAGS) ifeq ($(WOLFBOOT_SMALL_STACK),1) ifneq ($(SPMATH),1) STACK_USAGE=5008 @@ -340,7 +340,7 @@ ifeq ($(SIGN),RSAPSS3072) SIGN_ALG=RSAPSS3072 WOLFCRYPT_OBJS+= $(RSA_OBJS) WOLFCRYPT_OBJS+=$(MATH_OBJS) - CFLAGS+=-D"WOLFBOOT_SIGN_RSAPSS3072" $(RSA_EXTRA_CFLAGS) + CFLAGS+=-D"WOLFBOOT_SIGN_RSA3072" -DWOLFBOOT_SIGN_RSA_PSS $(RSA_EXTRA_CFLAGS) ifeq ($(WOLFBOOT_SMALL_STACK),1) ifneq ($(SPMATH),1) STACK_USAGE=5008 @@ -374,7 +374,7 @@ ifeq ($(SIGN),RSAPSS4096) SIGN_ALG=RSAPSS4096 WOLFCRYPT_OBJS+= $(RSA_OBJS) WOLFCRYPT_OBJS+=$(MATH_OBJS) - CFLAGS+=-D"WOLFBOOT_SIGN_RSAPSS4096" $(RSA_EXTRA_CFLAGS) + CFLAGS+=-D"WOLFBOOT_SIGN_RSA4096" -DWOLFBOOT_SIGN_RSA_PSS $(RSA_EXTRA_CFLAGS) ifeq ($(WOLFBOOT_SMALL_STACK),1) ifneq ($(SPMATH),1) STACK_USAGE=5888 @@ -591,7 +591,13 @@ ifneq ($(SIGN_SECONDARY),) SECONDARY_KEYGEN_OPTIONS=--$(LOWERCASE_SECONDARY) SECONDARY_SIGN_OPTIONS=--$(LOWERCASE_SECONDARY) CFLAGS+=-DSIGN_HYBRID - CFLAGS+=-DWOLFBOOT_SIGN_SECONDARY_$(SIGN_SECONDARY) + # SIGN_SECONDARY=RSAPSS{N} expands to RSA{N} size + RSA_PSS modifier + # (orthogonal: size and padding are independent at the macro level). + ifneq ($(filter RSAPSS%,$(SIGN_SECONDARY)),) + CFLAGS+=-DWOLFBOOT_SIGN_SECONDARY_$(subst RSAPSS,RSA,$(SIGN_SECONDARY)) -DWOLFBOOT_SIGN_SECONDARY_RSA_PSS + else + CFLAGS+=-DWOLFBOOT_SIGN_SECONDARY_$(SIGN_SECONDARY) + endif ifeq ($(SIGN_SECONDARY),RSA2048) WOLFCRYPT_OBJS+=$(RSA_OBJS) WOLFCRYPT_OBJS+=$(MATH_OBJS) @@ -1427,18 +1433,168 @@ ifneq ($(CERT_CHAIN_VERIFY),) ifeq ($(SIGN),ECC256) CERT_CHAIN_GEN_ALGO+=ecc256 endif + ifeq ($(SIGN),ECC384) + CERT_CHAIN_GEN_ALGO+=ecc384 + endif ifeq ($(SIGN),RSA2048) CERT_CHAIN_GEN_ALGO+=rsa2048 endif + ifeq ($(SIGN),RSA3072) + CERT_CHAIN_GEN_ALGO+=rsa3072 + endif ifeq ($(SIGN),RSA4096) CERT_CHAIN_GEN_ALGO+=rsa4096 - # Reasonably large default + endif + ifeq ($(SIGN),RSAPSS2048) + CERT_CHAIN_GEN_ALGO+=rsapss2048 + endif + ifeq ($(SIGN),RSAPSS3072) + CERT_CHAIN_GEN_ALGO+=rsapss3072 + endif + ifeq ($(SIGN),RSAPSS4096) + CERT_CHAIN_GEN_ALGO+=rsapss4096 + endif + + # If SIGN didn't match any of the cert-chain-supported algos above, + # fail loudly at make time instead of producing a malformed script + # invocation (empty --leaf-algo) that fails with a confusing error. + ifeq ($(strip $(CERT_CHAIN_GEN_ALGO)),) + $(error CERT_CHAIN_VERIFY=1 is not supported with SIGN=$(SIGN). \ + The dummy cert chain generator supports SIGN values: ECC256, \ + ECC384, RSA2048, RSA3072, RSA4096, RSAPSS2048, RSAPSS3072, \ + RSAPSS4096. Set USER_CERT_CHAIN= to supply a pre-built chain.) + endif + + # Per-level overrides for the dummy chain generator. Defaults: CA chain + # uses the same algo as the leaf (SIGN-derived), SHA256 for cert sigs. + # The leaf algo is fixed by SIGN — the leaf cert wraps the wolfBoot + # signing key, so it can't diverge. + CERT_CHAIN_GEN_CA_ALGO ?= $(CERT_CHAIN_GEN_ALGO) + CERT_CHAIN_GEN_CA_HASH ?= sha256 + + # If any chain component is RSA, the wolfHSM cert buffer must be + # large enough to hold an RSA4096 cert (~1.5-2 KB). + ifneq ($(filter rsa%,$(CERT_CHAIN_GEN_CA_ALGO) $(CERT_CHAIN_GEN_ALGO)),) CFLAGS += -DWOLFHSM_CFG_MAX_CERT_SIZE=4096 endif + + CERT_CHAIN_GEN_FLAGS := --ca-algo $(CERT_CHAIN_GEN_CA_ALGO) \ + --leaf-algo $(CERT_CHAIN_GEN_ALGO) \ + --ca-hash $(CERT_CHAIN_GEN_CA_HASH) + + # Auto-bridge: the verifier in the bootloader must support whatever + # algo and hash actually sign the dummy chain. Without this, a + # non-default GEN_CA_ALGO/GEN_CA_HASH builds successfully but fails at + # runtime when the matching wolfCrypt module is absent. `override` is + # required so a user-supplied AUX_PK_ALGOS/AUX_HASH_ALGOS on the make + # command line (which would otherwise be read-only) doesn't silently + # defeat the auto-bridge. + override AUX_PK_ALGOS += $(CERT_CHAIN_GEN_CA_ALGO) + override AUX_HASH_ALGOS += $(CERT_CHAIN_GEN_CA_HASH) endif SIGN_OPTIONS += --cert-chain $(CERT_CHAIN_FILE) endif +# Auxiliary wolfCrypt algorithms - compile in extra wolfCrypt code beyond +# what SIGN/HASH already pulls in. Decoupled from any specific feature; the +# cert chain verifier auto-populates these (see above), but the variables +# are also available as a generic primitive for any future feature that +# needs extra algo support compiled in. +# Usage: AUX_HASH_ALGOS=sha384,sha512 AUX_PK_ALGOS=rsa4096,rsapss4096,ecc256 +# +# RSA tokens are orthogonal along two axes: size (rsa2048/3072/4096) and +# padding mode (rsa* = PKCS#1 v1.5, rsapss* = PSS). Each rsapssN token +# enables both the RSAN size and PSS padding; mixing rsaN and rsapssN for +# the same size accepts either padding for that key length. +ifneq ($(strip $(AUX_PK_ALGOS)$(AUX_HASH_ALGOS)),) + comma := , + AUX_HASH_ALGOS_LIST := $(sort $(subst $(comma), ,$(AUX_HASH_ALGOS))) + AUX_PK_ALGOS_LIST := $(sort $(subst $(comma), ,$(AUX_PK_ALGOS))) + + # --- Hash algorithms --- + # SHA256 is always present in wolfCrypt for wolfBoot, no extra flag needed. + ifneq ($(filter sha384,$(AUX_HASH_ALGOS_LIST)),) + CFLAGS += -DWOLFBOOT_AUX_HASH_SHA384 + ifeq ($(filter %/sha512.o,$(WOLFCRYPT_OBJS)),) + WOLFCRYPT_OBJS += $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha512.o + endif + endif + ifneq ($(filter sha512,$(AUX_HASH_ALGOS_LIST)),) + CFLAGS += -DWOLFBOOT_AUX_HASH_SHA512 + ifeq ($(filter %/sha512.o,$(WOLFCRYPT_OBJS)),) + WOLFCRYPT_OBJS += $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha512.o + endif + endif + ifneq ($(filter sha3,$(AUX_HASH_ALGOS_LIST)),) + CFLAGS += -DWOLFBOOT_AUX_HASH_SHA3 + ifeq ($(filter %/sha3.o,$(WOLFCRYPT_OBJS)),) + WOLFCRYPT_OBJS += $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha3.o + endif + endif + + # --- PK algorithms --- + # RSA size flags - rsa{N} and rsapss{N} both select the same N-bit + # modulus support; padding is set separately below. + ifneq ($(filter rsa2048 rsapss2048,$(AUX_PK_ALGOS_LIST)),) + CFLAGS += -DWOLFBOOT_AUX_PK_RSA2048 + endif + ifneq ($(filter rsa3072 rsapss3072,$(AUX_PK_ALGOS_LIST)),) + CFLAGS += -DWOLFBOOT_AUX_PK_RSA3072 + endif + ifneq ($(filter rsa4096 rsapss4096,$(AUX_PK_ALGOS_LIST)),) + CFLAGS += -DWOLFBOOT_AUX_PK_RSA4096 + endif + # PSS padding - any rsapss* token enables PSS for all selected RSA sizes + ifneq ($(filter rsapss2048 rsapss3072 rsapss4096,$(AUX_PK_ALGOS_LIST)),) + CFLAGS += -DWOLFBOOT_AUX_PK_RSA_PSS + endif + # Add RSA objects if any RSA (PKCS#1 v1.5 or PSS) aux PK is requested + ifneq ($(filter rsa2048 rsa3072 rsa4096 rsapss2048 rsapss3072 rsapss4096,$(AUX_PK_ALGOS_LIST)),) + ifeq ($(filter %/rsa.o,$(WOLFCRYPT_OBJS)),) + WOLFCRYPT_OBJS += $(RSA_OBJS) + endif + ifeq ($(filter %/sp_int.o %/integer.o,$(WOLFCRYPT_OBJS)),) + WOLFCRYPT_OBJS += $(MATH_OBJS) + endif + endif + + ifneq ($(filter ecc256,$(AUX_PK_ALGOS_LIST)),) + CFLAGS += -DWOLFBOOT_AUX_PK_ECC256 + endif + ifneq ($(filter ecc384,$(AUX_PK_ALGOS_LIST)),) + CFLAGS += -DWOLFBOOT_AUX_PK_ECC384 + endif + ifneq ($(filter ecc521,$(AUX_PK_ALGOS_LIST)),) + CFLAGS += -DWOLFBOOT_AUX_PK_ECC521 + endif + # Add ECC objects if any ECC aux PK is requested + ifneq ($(filter ecc256 ecc384 ecc521,$(AUX_PK_ALGOS_LIST)),) + ifeq ($(filter %/ecc.o,$(WOLFCRYPT_OBJS)),) + WOLFCRYPT_OBJS += $(ECC_OBJS) + endif + ifeq ($(filter %/sp_int.o %/integer.o,$(WOLFCRYPT_OBJS)),) + WOLFCRYPT_OBJS += $(MATH_OBJS) + endif + endif + + ifneq ($(filter ed25519,$(AUX_PK_ALGOS_LIST)),) + CFLAGS += -DWOLFBOOT_AUX_PK_ED25519 + ifeq ($(filter %/ed25519.o,$(WOLFCRYPT_OBJS)),) + WOLFCRYPT_OBJS += $(ED25519_OBJS) + endif + endif + + ifneq ($(filter ed448,$(AUX_PK_ALGOS_LIST)),) + CFLAGS += -DWOLFBOOT_AUX_PK_ED448 + ifeq ($(filter %/ed448.o,$(WOLFCRYPT_OBJS)),) + WOLFCRYPT_OBJS += $(ED448_OBJS) + endif + ifeq ($(filter %/sha3.o,$(WOLFCRYPT_OBJS)),) + WOLFCRYPT_OBJS += $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha3.o + endif + endif +endif + # Clock Speed (Hz) ifneq ($(CLOCK_SPEED),) CFLAGS += -DCLOCK_SPEED=$(CLOCK_SPEED) diff --git a/src/image.c b/src/image.c index aee76a2874..91f1c34304 100644 --- a/src/image.c +++ b/src/image.c @@ -2395,10 +2395,7 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img) defined (WOLFBOOT_SIGN_RSA4096) || \ defined (WOLFBOOT_SIGN_RSA2048ENC) || \ defined (WOLFBOOT_SIGN_RSA3072ENC) || \ - defined (WOLFBOOT_SIGN_RSA4096ENC) || \ - defined (WOLFBOOT_SIGN_RSAPSS2048) || \ - defined (WOLFBOOT_SIGN_RSAPSS3072) || \ - defined (WOLFBOOT_SIGN_RSAPSS4096) + defined (WOLFBOOT_SIGN_RSA4096ENC) if (stored_signature_size != RSA_IMAGE_SIGNATURE_SIZE) return -1; #elif defined (WOLFBOOT_SIGN_ECC256) || \ @@ -2470,10 +2467,7 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img) defined (WOLFBOOT_SIGN_SECONDARY_RSA4096) || \ defined (WOLFBOOT_SIGN_SECONDARY_RSA2048ENC) || \ defined (WOLFBOOT_SIGN_SECONDARY_RSA3072ENC) || \ - defined (WOLFBOOT_SIGN_SECONDARY_RSA4096ENC) || \ - defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS2048) || \ - defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) || \ - defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS4096) + defined (WOLFBOOT_SIGN_SECONDARY_RSA4096ENC) expected_secondary_signature_size = RSA_IMAGE_SIGNATURE_SIZE; #elif defined (WOLFBOOT_SIGN_SECONDARY_ECC256) || \ defined (WOLFBOOT_SIGN_SECONDARY_ECC384) || \ diff --git a/src/xmalloc.c b/src/xmalloc.c index 358f3e5663..a52114b0ef 100644 --- a/src/xmalloc.c +++ b/src/xmalloc.c @@ -336,9 +336,7 @@ static struct xmalloc_slot xmalloc_pool[] = { #elif defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_RSA4096) || \ - defined(WOLFBOOT_SIGN_RSA3072) || \ - defined(WOLFBOOT_SIGN_RSAPSS2048) || defined(WOLFBOOT_SIGN_RSAPSS4096) || \ - defined(WOLFBOOT_SIGN_RSAPSS3072) + defined(WOLFBOOT_SIGN_RSA3072) #if defined(WOLFBOOT_HASH_SHA256) || defined(WOLFBOOT_HASH_SHA384) static uint32_t sha_block[HASH_BLOCK_SIZE]; @@ -352,7 +350,7 @@ static uint32_t sha_block[HASH_BLOCK_SIZE]; static uint8_t asncheck_buf[ASNCHECK_BUF_SIZE]; #ifndef USE_FAST_MATH - #if defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_RSAPSS2048) + #if defined(WOLFBOOT_SIGN_RSA2048) #define MP_SCHEME "SP RSA2048" #define MP_INT_DYNAMIC_SIZE MP_INT_SIZEOF(MP_BITS_CNT(2048)) #define MP_BIGINT_MODEXP_SIZE (MP_INT_DYNAMIC_SIZE * 4) @@ -365,7 +363,7 @@ static uint8_t asncheck_buf[ASNCHECK_BUF_SIZE]; #define MPDIGIT_BUF1_SIZE (MP_DIGIT_SIZE * (72 * 4 + 3)) static uint8_t mp_digit_buf1[MPDIGIT_BUF1_SIZE]; #endif - #elif defined(WOLFBOOT_SIGN_RSA3072) || defined(WOLFBOOT_SIGN_RSAPSS3072) + #elif defined(WOLFBOOT_SIGN_RSA3072) #define MP_SCHEME "SP RSA3072" #define MP_INT_DYNAMIC_SIZE MP_INT_SIZEOF(MP_BITS_CNT(3072)) #define MP_BIGINT_MODEXP_SIZE (MP_INT_DYNAMIC_SIZE * 4) diff --git a/tools/scripts/sim-gen-dummy-chain.sh b/tools/scripts/sim-gen-dummy-chain.sh index 3688f3190d..f1630861f6 100755 --- a/tools/scripts/sim-gen-dummy-chain.sh +++ b/tools/scripts/sim-gen-dummy-chain.sh @@ -1,67 +1,143 @@ #!/bin/bash -# Certificate Chain Generation Script (ECC P256 or RSA) +# Certificate Chain Generation Script (ECC P-256/P-384 or RSA) # Creates a certificate chain with root, intermediate, and leaf # Outputs DER format files plus C arrays for embedding # Optional: Use existing leaf private key with --leaf argument +# Optional: Use distinct algorithms for the CA chain vs the leaf identity +# (e.g. ECC256 root/intermediate signing an RSA4096 leaf cert) set -e # Exit on any error -# Default output directory and algorithm +# Default output directory and algorithms OUTPUT_DIR="test-dummy-ca" -ALGO="ecc256" # Default to ECC P-256 keys +CA_ALGO="ecc256" # Default to ECC P-256 keys for root + intermediate +LEAF_ALGO="" # Defaults to CA_ALGO if not explicitly set +CA_HASH="sha256" # Hash used for cert signatures throughout the chain + +# Whitelists +SUPPORTED_ALGOS="ecc256 ecc384 rsa2048 rsa3072 rsa4096 rsapss2048 rsapss3072 rsapss4096" +SUPPORTED_HASHES="sha256 sha384 sha512" + +is_supported_algo() { + local algo=$1 + for a in $SUPPORTED_ALGOS; do + if [[ "$a" == "$algo" ]]; then return 0; fi + done + return 1 +} -# Helper functions for key operations -generate_private_key() { - local output_file=$1 - - if [[ "$ALGO" == "ecc256" ]]; then - openssl ecparam -genkey -name prime256v1 -noout -out "$output_file" - elif [[ "$ALGO" == "rsa2048" ]]; then - openssl genrsa -out "$output_file" 2048 - elif [[ "$ALGO" == "rsa4096" ]]; then - openssl genrsa -out "$output_file" 4096 - fi +is_supported_hash() { + local h=$1 + for a in $SUPPORTED_HASHES; do + if [[ "$a" == "$h" ]]; then return 0; fi + done + return 1 } -convert_key_to_der() { - local input_file=$1 +# Helper functions for key operations. Each takes the target algo as $1. +# rsapss* tokens carry an RSA key plus a PSS padding intent — the key +# itself is generated/handled identically to plain rsa*. +generate_private_key() { + local algo=$1 local output_file=$2 - if [[ "$ALGO" == "ecc256" ]]; then - openssl ec -in "$input_file" -outform DER -out "$output_file" - elif [[ "$ALGO" == "rsa2048" || "$ALGO" == "rsa4096" ]]; then - openssl rsa -in "$input_file" -outform DER -out "$output_file" - fi + case "$algo" in + ecc256) openssl ecparam -genkey -name prime256v1 -noout -out "$output_file" ;; + ecc384) openssl ecparam -genkey -name secp384r1 -noout -out "$output_file" ;; + rsa2048|rsapss2048) openssl genrsa -out "$output_file" 2048 ;; + rsa3072|rsapss3072) openssl genrsa -out "$output_file" 3072 ;; + rsa4096|rsapss4096) openssl genrsa -out "$output_file" 4096 ;; + *) echo "Unsupported algo: $algo" >&2; exit 1 ;; + esac +} + +convert_key_to_der() { + local algo=$1 + local input_file=$2 + local output_file=$3 + + case "$algo" in + ecc256|ecc384) + openssl ec -in "$input_file" -outform DER -out "$output_file" + ;; + rsa2048|rsa3072|rsa4096|rsapss2048|rsapss3072|rsapss4096) + openssl rsa -in "$input_file" -outform DER -out "$output_file" + ;; + *) echo "Unsupported algo: $algo" >&2; exit 1 ;; + esac } extract_public_key() { - local cert_file=$1 - local pubkey_pem=$2 - local pubkey_der=$3 + local algo=$1 + local cert_file=$2 + local pubkey_pem=$3 + local pubkey_der=$4 - # Extract public key from certificate (same for both algos) + # Extract public key from certificate (same command for all supported algos) openssl x509 -in "$cert_file" -pubkey -noout > "$pubkey_pem" # Convert public key to DER format - if [[ "$ALGO" == "ecc256" ]]; then - openssl ec -pubin -in "$pubkey_pem" -outform DER -out "$pubkey_der" - elif [[ "$ALGO" == "rsa2048" || "$ALGO" == "rsa4096" ]]; then - openssl rsa -pubin -in "$pubkey_pem" -outform DER -out "$pubkey_der" - fi + case "$algo" in + ecc256|ecc384) + openssl ec -pubin -in "$pubkey_pem" -outform DER -out "$pubkey_der" + ;; + rsa2048|rsa3072|rsa4096|rsapss2048|rsapss3072|rsapss4096) + openssl rsa -pubin -in "$pubkey_pem" -outform DER -out "$pubkey_der" + ;; + *) echo "Unsupported algo: $algo" >&2; exit 1 ;; + esac } validate_key_format() { - local key_file=$1 + local algo=$1 + local key_file=$2 + + case "$algo" in + ecc256|ecc384) + openssl ec -in "$key_file" -noout + ;; + rsa2048|rsa3072|rsa4096|rsapss2048|rsapss3072|rsapss4096) + openssl rsa -in "$key_file" -noout + ;; + *) echo "Unsupported algo: $algo" >&2; exit 1 ;; + esac +} - if [[ "$ALGO" == "ecc256" ]]; then - openssl ec -in "$key_file" -noout - elif [[ "$ALGO" == "rsa2048" || "$ALGO" == "rsa4096" ]]; then - openssl rsa -in "$key_file" -noout - fi +# Build openssl -sigopt args for a given algo + hash. For rsapss* algos +# we ask openssl to use PSS padding with salt length equal to the digest +# length and MGF1 keyed by the same hash (the standard interoperable +# choice that wolfCrypt's PSS verify expects). Empty for non-PSS algos. +sig_opts_for_algo() { + local algo=$1 + local hash=$2 + case "$algo" in + rsapss*) + echo "-sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest -sigopt rsa_mgf1_md:$hash" + ;; + esac +} + +usage() { + cat < Set both CA and leaf algos (legacy single-algo shortcut). + --ca-algo Algo for root + intermediate keys. + --leaf-algo Algo for the leaf key (defaults to --ca-algo). + --ca-hash Hash used for cert signatures (default: sha256). + --leaf Use existing leaf private key (must match --leaf-algo). + --outdir Output directory (default: test-dummy-ca). + +Supported algos: $SUPPORTED_ALGOS +Supported hashes: $SUPPORTED_HASHES +EOT } # Parse command line arguments LEAF_KEY_FILE="" +ALGO_LEGACY_SET=0 +ALGO_PER_LEVEL_SET=0 while [[ $# -gt 0 ]]; do case $1 in --leaf) @@ -73,21 +149,72 @@ while [[ $# -gt 0 ]]; do shift 2 ;; --algo) - ALGO="$2" - if [[ "$ALGO" != "ecc256" && "$ALGO" != "rsa2048" && "$ALGO" != "rsa4096" ]]; then - echo "Invalid algorithm: $ALGO. Use 'ecc256', 'rsa2048', or 'rsa4096'" + if ! is_supported_algo "$2"; then + echo "Invalid algorithm: $2. Supported: $SUPPORTED_ALGOS" >&2 exit 1 fi + CA_ALGO="$2" + LEAF_ALGO="$2" + ALGO_LEGACY_SET=1 shift 2 ;; + --ca-algo) + if ! is_supported_algo "$2"; then + echo "Invalid CA algorithm: $2. Supported: $SUPPORTED_ALGOS" >&2 + exit 1 + fi + CA_ALGO="$2" + ALGO_PER_LEVEL_SET=1 + shift 2 + ;; + --leaf-algo) + if ! is_supported_algo "$2"; then + echo "Invalid leaf algorithm: $2. Supported: $SUPPORTED_ALGOS" >&2 + exit 1 + fi + LEAF_ALGO="$2" + ALGO_PER_LEVEL_SET=1 + shift 2 + ;; + --ca-hash) + if ! is_supported_hash "$2"; then + echo "Invalid CA hash: $2. Supported: $SUPPORTED_HASHES" >&2 + exit 1 + fi + CA_HASH="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; *) echo "Unknown option: $1" - echo "Usage: $0 [--leaf ] [--outdir ] [--algo ]" + usage exit 1 ;; esac done +if [ "$ALGO_LEGACY_SET" -eq 1 ] && [ "$ALGO_PER_LEVEL_SET" -eq 1 ]; then + echo "Error: --algo cannot be combined with --ca-algo or --leaf-algo." >&2 + echo " Use --algo as a shortcut to set both, or use the per-level flags." >&2 + exit 1 +fi + +# Default leaf algo to CA algo when not explicitly set +if [[ -z "$LEAF_ALGO" ]]; then + LEAF_ALGO="$CA_ALGO" +fi + +# Resolve PSS sigopts once based on the selected algos. CA_SIG_OPTS +# applies to every cert signed by a CA-tier key (root self-sig, +# intermediate CSR self-sig, intermediate cert, leaf cert); LEAF_SIG_OPTS +# applies to the leaf CSR self-sig only (the leaf cert itself is signed +# by the intermediate's CA-tier key). Both are empty for non-PSS algos. +CA_SIG_OPTS=$(sig_opts_for_algo "$CA_ALGO" "$CA_HASH") +LEAF_SIG_OPTS=$(sig_opts_for_algo "$LEAF_ALGO" "$CA_HASH") + # Configuration ROOT_SUBJECT="/C=US/ST=California/L=San Francisco/O=MyOrganization/OU=Root CA/CN=My Root CA" INTERMEDIATE_SUBJECT="/C=US/ST=California/L=San Francisco/O=MyOrganization/OU=Intermediate CA/CN=My Intermediate CA" @@ -100,14 +227,14 @@ mkdir -p ${OUTPUT_DIR}/temp ################## # GENERATE CHAIN ################## -echo "Generating Certificate Chain using $ALGO..." +echo "Generating Certificate Chain (ca-algo=$CA_ALGO, leaf-algo=$LEAF_ALGO, ca-hash=$CA_HASH)..." -# Step 1: Generate Root key and certificate +# Step 1: Generate Root key and certificate (CA algo) echo "Generating Root CA..." -generate_private_key "${OUTPUT_DIR}/temp/root.key.pem" +generate_private_key "$CA_ALGO" "${OUTPUT_DIR}/temp/root.key.pem" # Create PEM format root certificate (temporary) -openssl req -new -x509 -days 3650 -sha256 \ +openssl req -new -x509 -days 3650 -$CA_HASH $CA_SIG_OPTS \ -key ${OUTPUT_DIR}/temp/root.key.pem \ -out ${OUTPUT_DIR}/temp/root.crt.pem \ -subj "$ROOT_SUBJECT" \ @@ -115,20 +242,20 @@ openssl req -new -x509 -days 3650 -sha256 \ -addext "keyUsage=critical,keyCertSign,cRLSign,digitalSignature" # Convert root key and certificate to DER format -convert_key_to_der "${OUTPUT_DIR}/temp/root.key.pem" "${OUTPUT_DIR}/root-prvkey.der" +convert_key_to_der "$CA_ALGO" "${OUTPUT_DIR}/temp/root.key.pem" "${OUTPUT_DIR}/root-prvkey.der" openssl x509 -in ${OUTPUT_DIR}/temp/root.crt.pem -outform DER -out ${OUTPUT_DIR}/root-cert.der -# Step 2: Generate Intermediate key and CSR +# Step 2: Generate Intermediate key and CSR (CA algo) echo "Generating Intermediate CA..." -generate_private_key "${OUTPUT_DIR}/temp/intermediate.key.pem" +generate_private_key "$CA_ALGO" "${OUTPUT_DIR}/temp/intermediate.key.pem" -openssl req -new -sha256 \ +openssl req -new -$CA_HASH $CA_SIG_OPTS \ -key ${OUTPUT_DIR}/temp/intermediate.key.pem \ -out ${OUTPUT_DIR}/temp/intermediate.csr \ -subj "$INTERMEDIATE_SUBJECT" # Step 3: Sign Intermediate certificate with Root -openssl x509 -req -days 1825 -sha256 \ +openssl x509 -req -days 1825 -$CA_HASH $CA_SIG_OPTS \ -in ${OUTPUT_DIR}/temp/intermediate.csr \ -out ${OUTPUT_DIR}/temp/intermediate.crt.pem \ -CA ${OUTPUT_DIR}/temp/root.crt.pem \ @@ -137,29 +264,31 @@ openssl x509 -req -days 1825 -sha256 \ -extfile <(printf "basicConstraints=critical,CA:TRUE,pathlen:0\nkeyUsage=critical,keyCertSign,cRLSign,digitalSignature") # Convert intermediate key and certificate to DER format -convert_key_to_der "${OUTPUT_DIR}/temp/intermediate.key.pem" "${OUTPUT_DIR}/intermediate-prvkey.der" +convert_key_to_der "$CA_ALGO" "${OUTPUT_DIR}/temp/intermediate.key.pem" "${OUTPUT_DIR}/intermediate-prvkey.der" openssl x509 -in ${OUTPUT_DIR}/temp/intermediate.crt.pem -outform DER -out ${OUTPUT_DIR}/intermediate-cert.der -# Step 4: Handle Leaf key (generate or use existing) +# Step 4: Handle Leaf key (generate or use existing) - LEAF algo echo "Handling Leaf Certificate..." if [ -z "$LEAF_KEY_FILE" ]; then - echo "Generating new leaf private key..." - generate_private_key "${OUTPUT_DIR}/temp/leaf.key.pem" + echo "Generating new leaf private key (algo=$LEAF_ALGO)..." + generate_private_key "$LEAF_ALGO" "${OUTPUT_DIR}/temp/leaf.key.pem" else - echo "Using provided leaf private key: $LEAF_KEY_FILE" + echo "Using provided leaf private key: $LEAF_KEY_FILE (algo=$LEAF_ALGO)" cp "$LEAF_KEY_FILE" ${OUTPUT_DIR}/temp/leaf.key.pem - # Ensure the key file is in the right format - validate_key_format "${OUTPUT_DIR}/temp/leaf.key.pem" + # Ensure the key file is in the right format for the declared leaf algo + validate_key_format "$LEAF_ALGO" "${OUTPUT_DIR}/temp/leaf.key.pem" fi -# Create CSR for leaf certificate -openssl req -new -sha256 \ +# Create CSR for leaf certificate (CSR is signed by leaf key, but the +# resulting cert signature is set by the CA when signing - so the CSR +# self-signature uses CA_HASH for consistency). +openssl req -new -$CA_HASH $LEAF_SIG_OPTS \ -key ${OUTPUT_DIR}/temp/leaf.key.pem \ -out ${OUTPUT_DIR}/temp/leaf.csr \ -subj "$LEAF_SUBJECT" -# Step 5: Sign Leaf certificate with Intermediate -openssl x509 -req -days 365 -sha256 \ +# Step 5: Sign Leaf certificate with Intermediate (uses CA_HASH) +openssl x509 -req -days 365 -$CA_HASH $CA_SIG_OPTS \ -in ${OUTPUT_DIR}/temp/leaf.csr \ -out ${OUTPUT_DIR}/temp/leaf.crt.pem \ -CA ${OUTPUT_DIR}/temp/intermediate.crt.pem \ @@ -168,12 +297,12 @@ openssl x509 -req -days 365 -sha256 \ -extfile <(printf "basicConstraints=CA:FALSE\nkeyUsage=critical,digitalSignature,keyEncipherment\nextendedKeyUsage=serverAuth") # Convert leaf key and certificate to DER format -convert_key_to_der "${OUTPUT_DIR}/temp/leaf.key.pem" "${OUTPUT_DIR}/leaf-prvkey.der" +convert_key_to_der "$LEAF_ALGO" "${OUTPUT_DIR}/temp/leaf.key.pem" "${OUTPUT_DIR}/leaf-prvkey.der" openssl x509 -in ${OUTPUT_DIR}/temp/leaf.crt.pem -outform DER -out ${OUTPUT_DIR}/leaf-cert.der # Extract the public key from leaf certificate in DER format echo "Extracting public key from leaf certificate..." -extract_public_key "${OUTPUT_DIR}/temp/leaf.crt.pem" "${OUTPUT_DIR}/temp/leaf_pubkey.pem" "${OUTPUT_DIR}/leaf-pubkey.der" +extract_public_key "$LEAF_ALGO" "${OUTPUT_DIR}/temp/leaf.crt.pem" "${OUTPUT_DIR}/temp/leaf_pubkey.pem" "${OUTPUT_DIR}/leaf-pubkey.der" # Create raw DER format certificate chain cat ${OUTPUT_DIR}/intermediate-cert.der ${OUTPUT_DIR}/leaf-cert.der > ${OUTPUT_DIR}/raw-chain.der @@ -250,7 +379,7 @@ openssl verify -CAfile ${OUTPUT_DIR}/temp/root.crt.pem -untrusted ${OUTPUT_DIR}/ echo "" echo "=== Generated Files Summary ===" echo "" -echo "DER Format (Algorithm: $ALGO):" +echo "DER Format (CA algo: $CA_ALGO, leaf algo: $LEAF_ALGO, CA hash: $CA_HASH):" echo " Root CA certificate: ${OUTPUT_DIR}/root-cert.der" echo " Root CA key: ${OUTPUT_DIR}/root-prvkey.der" echo " Intermediate certificate: ${OUTPUT_DIR}/intermediate-cert.der"