From 9737cfc82e4c3b3f8e87a4d38d4f23b8f97d2497 Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Thu, 9 Jul 2026 11:25:39 -0700 Subject: [PATCH] Add support for auxillary algorithms and cert chain/TPM usage --- .github/workflows/test-configs.yml | 28 +++ .github/workflows/test-wolfhsm-simulator.yml | 5 + Makefile | 2 +- ...wolfHSM-server-certchain-rsa2048-ca.config | 36 +++ docs/TPM.md | 6 + docs/compile.md | 43 ++++ docs/wolfHSM.md | 15 ++ include/user_settings.h | 230 ++++++++++++------ options.mk | 167 ++++++++++++- tools/config.mk | 4 + tools/scripts/sim-gen-dummy-chain.sh | 131 ++++++---- 11 files changed, 537 insertions(+), 130 deletions(-) create mode 100644 config/examples/sim-wolfHSM-server-certchain-rsa2048-ca.config diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index 54c2309046..aec2dd2ab1 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -804,6 +804,34 @@ jobs: arch: host config-file: ./config/examples/sim-wolfHSM-server-certchain-rsa4096.config + sim_wolfhsm_server_certchain_rsa2048_ca_test: + uses: ./.github/workflows/test-build.yml + with: + arch: host + config-file: ./config/examples/sim-wolfHSM-server-certchain-rsa2048-ca.config + + # Auxiliary algorithms (AUX_PK_ALGOS/AUX_HASH_ALGOS) compile-only matrix + sim_aux_algos_ed25519_rsa_ecc_test: + uses: ./.github/workflows/test-build.yml + with: + arch: host + config-file: ./config/examples/sim.config + make-args: AUX_PK_ALGOS=rsa4096,ecc384 AUX_HASH_ALGOS=sha384,sha512 + + sim_aux_algos_multi_rsa_test: + uses: ./.github/workflows/test-build.yml + with: + arch: host + config-file: ./config/examples/sim.config + make-args: SIGN=RSA2048 AUX_PK_ALGOS=rsa4096,rsapss2048,ecc521,ed448 AUX_HASH_ALGOS=sha3 + + sim_aux_algos_tpm_fallback_test: + uses: ./.github/workflows/test-build.yml + with: + arch: host + config-file: ./config/examples/sim.config + make-args: WOLFTPM=1 SIM_TPM=1 + rp2350_test: uses: ./.github/workflows/test-build-pico-sdk.yml with: diff --git a/.github/workflows/test-wolfhsm-simulator.yml b/.github/workflows/test-wolfhsm-simulator.yml index 35e40da06e..c0a05b414e 100644 --- a/.github/workflows/test-wolfhsm-simulator.yml +++ b/.github/workflows/test-wolfhsm-simulator.yml @@ -44,6 +44,11 @@ jobs: needs_posix_server: false posix_server_nvminit: false needs_nvm_image: true + - name: "wolfHSM server cert chain verify RSA2048/SHA384 CA, ECC256 leaf" + file: "config/examples/sim-wolfHSM-server-certchain-rsa2048-ca.config" + needs_posix_server: false + posix_server_nvminit: false + needs_nvm_image: true fail-fast: false diff --git a/Makefile b/Makefile index 2051b7900a..a011889aac 100644 --- a/Makefile +++ b/Makefile @@ -432,7 +432,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 --leaf-algo $(CERT_CHAIN_GEN_LEAF_ALGO) --ca-algo $(CERT_CHAIN_GEN_CA_ALGO) --ca-hash $(CERT_CHAIN_GEN_CA_HASH) --leaf wolfboot_signing_private_key.der) else @echo "Using user-provided private key: $(USER_PRIVATE_KEY)" endif diff --git a/config/examples/sim-wolfHSM-server-certchain-rsa2048-ca.config b/config/examples/sim-wolfHSM-server-certchain-rsa2048-ca.config new file mode 100644 index 0000000000..42c8fffdb1 --- /dev/null +++ b/config/examples/sim-wolfHSM-server-certchain-rsa2048-ca.config @@ -0,0 +1,36 @@ +ARCH=sim +TARGET=sim +SIGN?=ECC256 +HASH?=SHA256 +WOLFBOOT_SMALL_STACK?=0 +SPI_FLASH=0 +DEBUG=0 +SPMATH=1 + +# Cert chain options: leaf follows SIGN (ecc256), CA certs use RSA2048 +# signed with SHA-384. The CA algorithms are compiled in automatically +# through AUX_PK_ALGOS/AUX_HASH_ALGOS. +CERT_CHAIN_VERIFY=1 +CERT_CHAIN_GEN_CA_ALGO=rsa2048 +CERT_CHAIN_GEN_CA_HASH=sha384 + +# Ensure header is large enough to hold the cert chain (check sign tool output) +# for actual length +IMAGE_HEADER_SIZE=4096 + +# 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 +# if on external flash, it should be multiple of system page size +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/docs/TPM.md b/docs/TPM.md index c25b279536..512f0a8ec3 100644 --- a/docs/TPM.md +++ b/docs/TPM.md @@ -21,6 +21,12 @@ In wolfBoot we support TPM based root of trust, sealing/unsealing, cryptographic | (header macro) | `WOLFBOOT_TPM_MFG_AIK_AUTH` / `WOLFBOOT_TPM_MFG_EH_AUTH` | Default (precomputed) mode: the 16-byte per-device AIK / EH authValues (placeholder `0xFF` default). | | (header macro) | `WOLFBOOT_TPM_MFG_EH_MASTER` | Derive mode: override the endorsement-hierarchy master value (16-byte initializer list, sample default). | +The TPM primary key (SRK) and parameter encryption need ECC or RSA in +wolfCrypt. When `WOLFTPM=1` and neither `SIGN`, `SIGN_SECONDARY`, nor +`AUX_PK_ALGOS` provides one (e.g. `SIGN=ML_DSA` or `SIGN=ED25519`), the build +automatically adds `ecc256` to `AUX_PK_ALGOS`. See "Auxiliary crypto +algorithms" in [docs/compile.md](compile.md). + ## TPM manufacturing identity (IAK / IDevID authValue) When `WOLFTPM_MFG_IDENTITY` is enabled, `wolfBoot_tpm2_get_aik()` and diff --git a/docs/compile.md b/docs/compile.md index 4081d837d9..4f07f48885 100644 --- a/docs/compile.md +++ b/docs/compile.md @@ -142,6 +142,49 @@ It's possible to disable authentication of the firmware image by explicitly usin in the Makefile commandline. This will compile a minimal bootloader with no support for public-key authenticated secure boot. +### Auxiliary crypto algorithms + +Some features need crypto algorithms beyond the ones selected with `SIGN` and +`HASH`: certificate-chain verification may meet CA certificates signed with a +different algorithm than the leaf, and TPM support needs ECC or RSA for its +primary key and parameter encryption even when `SIGN` is a post-quantum or +Ed25519/Ed448 algorithm. + +`AUX_PK_ALGOS` and `AUX_HASH_ALGOS` compile extra wolfCrypt algorithms into +wolfBoot for those uses. Both take a comma-separated, case-insensitive list +(no spaces): + +```sh +make SIGN=ECC256 AUX_PK_ALGOS=rsa2048,ecc384 AUX_HASH_ALGOS=sha384 +``` + +Valid `AUX_PK_ALGOS` entries: `ecc256`, `ecc384`, `ecc521`, `rsa2048`, +`rsa3072`, `rsa4096`, `rsapss2048`, `rsapss3072`, `rsapss4096`, `ed25519`, +`ed448`. Valid `AUX_HASH_ALGOS` entries: `sha256`, `sha384`, `sha512`, +`sha3`. Post-quantum algorithms (ML-DSA, LMS, XMSS) are not yet supported as +auxiliary algorithms. + +Auxiliary algorithms are never used to verify the firmware image signature; +that remains bound to `SIGN` (and `SIGN_SECONDARY` for hybrid mode). Each +entry defines a `WOLFBOOT_AUX_PK_` or `WOLFBOOT_AUX_HASH_` macro +and links the matching wolfCrypt objects; entries already covered by +`SIGN`/`SIGN_SECONDARY` are no-ops. + +Notes: + +- Not compatible with `WOLFBOOT_SMALL_STACK=1`: the static memory pools are + sized for the primary `SIGN` algorithm only. +- `STACK_USAGE` is not adjusted automatically. If an auxiliary algorithm with + larger stack needs (e.g. `rsa4096`) is actually exercised, consider + `WOLFBOOT_HUGE_STACK=1` (the RSA4096 cert-chain configurations already do). +- When `WOLFTPM=1` and neither `SIGN`, `SIGN_SECONDARY`, nor `AUX_PK_ALGOS` + provides an ECC or RSA algorithm, `ecc256` is added automatically so the + TPM SRK and parameter encryption work. +- With `CERT_CHAIN_VERIFY=1`, the generated dummy chain's CA algorithms + (`CERT_CHAIN_GEN_CA_ALGO`/`CERT_CHAIN_GEN_CA_HASH`) are bridged into these + lists automatically. Users providing `USER_CERT_CHAIN` must list their + chain's algorithms here themselves. + ### Incremental updates wolfBoot support incremental updates. To enable this feature, compile with `DELTA_UPDATES=1`. diff --git a/docs/wolfHSM.md b/docs/wolfHSM.md index db42170bc2..8774d98fe0 100644 --- a/docs/wolfHSM.md +++ b/docs/wolfHSM.md @@ -62,10 +62,25 @@ To use certificate verification with wolfHSM: 3. Pre-provision one or more root CA certificates on the wolfHSM server at the NVM IDs listed in the HAL `hsmNvmIdCertRootCAList`. Verification succeeds if the embedded chain anchors to *any* root in the list (absent NVM IDs are silently skipped). The list length must not exceed `WOLFHSM_CFG_CERT_MAX_VERIFY_ROOTS` (default 8). 4. Sign firmware images with the `--cert-chain` option, providing a DER-encoded certificate chain +The chain's CA certificates do not need to use the same algorithm as the leaf +(which wraps the wolfBoot signing key and therefore follows `SIGN`). The +verifier only needs the CA algorithms compiled in, via `AUX_PK_ALGOS` and +`AUX_HASH_ALGOS` (see [docs/compile.md](compile.md)): + +- When the build auto-generates the dummy test chain (no `USER_CERT_CHAIN`), + the CA key algorithm and cert signature hash can be chosen with + `CERT_CHAIN_GEN_CA_ALGO` (`ecc256`/`ecc384`/`ecc521`/`rsa2048`/`rsa3072`/ + `rsa4096`, default: same as the leaf) and `CERT_CHAIN_GEN_CA_HASH` + (`sha256`/`sha384`/`sha512`, default `sha256`). Both are bridged into the + auxiliary algorithm lists automatically. +- When providing `USER_CERT_CHAIN`, set `AUX_PK_ALGOS`/`AUX_HASH_ALGOS` to + cover every algorithm used by the chain's certificate signatures. + To build the simulator using wolfHSM for certificate verification: - **Client Mode**: Use [config/examples/sim-wolfHSM-client-certchain.config](config/examples/sim-wolfHSM-client-certchain.config) - **Server Mode**: Use [config/examples/sim-wolfHSM-server-certchain.config](config/examples/sim-wolfHSM-server-certchain.config) +- **Server Mode, mixed-algorithm chain**: Use [config/examples/sim-wolfHSM-server-certchain-rsa2048-ca.config](../config/examples/sim-wolfHSM-server-certchain-rsa2048-ca.config) (RSA2048/SHA-384 CA certs, ECC256 leaf) ## Configuration Options diff --git a/include/user_settings.h b/include/user_settings.h index 7886a1bcc2..5c59ae3ed1 100644 --- a/include/user_settings.h +++ b/include/user_settings.h @@ -73,8 +73,80 @@ extern int tolower(int c); #define CUSTOM_RAND_GENERATE_SEED hal_trng_get_entropy #endif +/* Algorithm inclusion union. + * An algorithm's wolfCrypt code is compiled in when it is the primary + * (WOLFBOOT_SIGN_*), secondary (WOLFBOOT_SIGN_SECONDARY_*), or auxiliary + * (WOLFBOOT_AUX_PK_* / WOLFBOOT_AUX_HASH_*, from the AUX_PK_ALGOS and + * AUX_HASH_ALGOS build options) algorithm. Auxiliary algorithms serve + * features like cert-chain verification and TPM; they are never used to + * verify image signatures, which stay keyed on WOLFBOOT_SIGN_*. + * The WOLFBOOT_ENABLE_* macros below are internal to this file. */ +#if defined(WOLFBOOT_SIGN_ECC256) || defined(WOLFBOOT_SIGN_SECONDARY_ECC256) \ + || defined(WOLFBOOT_AUX_PK_ECC256) +# define WOLFBOOT_ENABLE_PK_ECC256 +#endif +#if defined(WOLFBOOT_SIGN_ECC384) || defined(WOLFBOOT_SIGN_SECONDARY_ECC384) \ + || defined(WOLFBOOT_AUX_PK_ECC384) +# define WOLFBOOT_ENABLE_PK_ECC384 +#endif +#if defined(WOLFBOOT_SIGN_ECC521) || defined(WOLFBOOT_SIGN_SECONDARY_ECC521) \ + || defined(WOLFBOOT_AUX_PK_ECC521) +# define WOLFBOOT_ENABLE_PK_ECC521 +#endif +#if defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_SECONDARY_RSA2048) \ + || defined(WOLFBOOT_AUX_PK_RSA2048) +# define WOLFBOOT_ENABLE_PK_RSA2048 +#endif +#if defined(WOLFBOOT_SIGN_RSA3072) || defined(WOLFBOOT_SIGN_SECONDARY_RSA3072) \ + || defined(WOLFBOOT_AUX_PK_RSA3072) +# define WOLFBOOT_ENABLE_PK_RSA3072 +#endif +#if defined(WOLFBOOT_SIGN_RSA4096) || defined(WOLFBOOT_SIGN_SECONDARY_RSA4096) \ + || defined(WOLFBOOT_AUX_PK_RSA4096) +# define WOLFBOOT_ENABLE_PK_RSA4096 +#endif +#if defined(WOLFBOOT_SIGN_RSAPSS2048) || \ + defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS2048) || \ + defined(WOLFBOOT_AUX_PK_RSAPSS2048) +# define WOLFBOOT_ENABLE_PK_RSAPSS2048 +#endif +#if defined(WOLFBOOT_SIGN_RSAPSS3072) || \ + defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) || \ + defined(WOLFBOOT_AUX_PK_RSAPSS3072) +# define WOLFBOOT_ENABLE_PK_RSAPSS3072 +#endif +#if defined(WOLFBOOT_SIGN_RSAPSS4096) || \ + defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS4096) || \ + defined(WOLFBOOT_AUX_PK_RSAPSS4096) +# define WOLFBOOT_ENABLE_PK_RSAPSS4096 +#endif +#if defined(WOLFBOOT_SIGN_ED25519) || defined(WOLFBOOT_SIGN_SECONDARY_ED25519) \ + || defined(WOLFBOOT_AUX_PK_ED25519) +# define WOLFBOOT_ENABLE_PK_ED25519 +#endif +#if defined(WOLFBOOT_SIGN_ED448) || defined(WOLFBOOT_SIGN_SECONDARY_ED448) \ + || defined(WOLFBOOT_AUX_PK_ED448) +# define WOLFBOOT_ENABLE_PK_ED448 +#endif + +/* Hash inclusion: the primary image hash plus any auxiliary hashes. + * SHA-256 is the default image hash when no other primary hash is set. */ +#if defined(WOLFBOOT_AUX_HASH_SHA256) || \ + (!defined(WOLFBOOT_HASH_SHA384) && !defined(WOLFBOOT_HASH_SHA3_384)) +# define WOLFBOOT_ENABLE_HASH_SHA256 +#endif +#if defined(WOLFBOOT_HASH_SHA384) || defined(WOLFBOOT_AUX_HASH_SHA384) +# define WOLFBOOT_ENABLE_HASH_SHA384 +#endif +#ifdef WOLFBOOT_AUX_HASH_SHA512 +# define WOLFBOOT_ENABLE_HASH_SHA512 +#endif +#if defined(WOLFBOOT_HASH_SHA3_384) || defined(WOLFBOOT_AUX_HASH_SHA3) +# define WOLFBOOT_ENABLE_HASH_SHA3 +#endif + /* ED25519 and SHA512 */ -#if defined(WOLFBOOT_SIGN_ED25519) || defined(WOLFBOOT_SIGN_SECONDARY_ED25519) +#ifdef WOLFBOOT_ENABLE_PK_ED25519 # define HAVE_ED25519 # define ED25519_SMALL # if !defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) @@ -86,7 +158,7 @@ extern int tolower(int c); #endif /* ED448 and SHA3/SHAKE256 */ -#if defined(WOLFBOOT_SIGN_ED448) || defined(WOLFBOOT_SIGN_SECONDARY_ED448) +#ifdef WOLFBOOT_ENABLE_PK_ED448 # define HAVE_ED448 # define HAVE_ED448_VERIFY # define ED448_SMALL @@ -100,12 +172,9 @@ extern int tolower(int c); #endif /* ECC */ -#if defined(WOLFBOOT_SIGN_ECC256) || \ - defined(WOLFBOOT_SIGN_ECC384) || \ - defined(WOLFBOOT_SIGN_ECC521) || \ - defined(WOLFBOOT_SIGN_SECONDARY_ECC256) || \ - defined(WOLFBOOT_SIGN_SECONDARY_ECC384) || \ - defined(WOLFBOOT_SIGN_SECONDARY_ECC521) || \ +#if defined(WOLFBOOT_ENABLE_PK_ECC256) || \ + defined(WOLFBOOT_ENABLE_PK_ECC384) || \ + defined(WOLFBOOT_ENABLE_PK_ECC521) || \ defined(WOLFCRYPT_SECURE_MODE) || \ defined(WOLFCRYPT_TEST) || defined(WOLFCRYPT_BENCHMARK) @@ -166,21 +235,18 @@ extern int tolower(int c); #define WOLFSSL_PUBLIC_MP /* Curve */ -# if defined(WOLFBOOT_SIGN_ECC256) || defined(WOLFCRYPT_SECURE_MODE) || \ - defined(WOLFBOOT_SIGN_SECONDARY_ECC256) || \ +# if defined(WOLFBOOT_ENABLE_PK_ECC256) || defined(WOLFCRYPT_SECURE_MODE) || \ defined(WOLFCRYPT_TEST) || defined(WOLFCRYPT_BENCHMARK) # define HAVE_ECC256 # endif -# if defined(WOLFBOOT_SIGN_ECC384) || \ - defined(WOLFBOOT_SIGN_SECONDARY_ECC384) || \ +# if defined(WOLFBOOT_ENABLE_PK_ECC384) || \ defined(WOLFCRYPT_SECURE_MODE) || \ defined(WOLFCRYPT_TEST) || defined(WOLFCRYPT_BENCHMARK) # define HAVE_ECC384 # define WOLFSSL_SP_384 # endif /* ECC521 only enabled if specifically requested (not for tests - too large) */ -# if defined(WOLFBOOT_SIGN_ECC521) || \ - defined(WOLFBOOT_SIGN_SECONDARY_ECC521) || \ +# if defined(WOLFBOOT_ENABLE_PK_ECC521) || \ defined(WOLFCRYPT_SECURE_MODE) # define HAVE_ECC521 # define WOLFSSL_SP_521 @@ -210,24 +276,17 @@ extern int tolower(int c); # define WOLFSSL_SP_NO_256 # endif # endif -#endif /* WOLFBOOT_SIGN_ECC521 || WOLFBOOT_SIGN_ECC384 || WOLFBOOT_SIGN_ECC256 || - * WOLFBOOT_SIGN_SECONDARY_ECC521 || WOLFBOOT_SIGN_SECONDARY_ECC384 || - * WOLFBOOT_SIGN_SECONDARY_ECC256 || WOLFCRYPT_SECURE_MODE */ +#endif /* WOLFBOOT_ENABLE_PK_ECC521 || WOLFBOOT_ENABLE_PK_ECC384 || + * WOLFBOOT_ENABLE_PK_ECC256 || WOLFCRYPT_SECURE_MODE */ /* RSA */ -#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) || \ +#if defined(WOLFBOOT_ENABLE_PK_RSA2048) || \ + defined(WOLFBOOT_ENABLE_PK_RSA3072) || \ + defined(WOLFBOOT_ENABLE_PK_RSA4096) || \ + defined(WOLFBOOT_ENABLE_PK_RSAPSS2048) || \ + defined(WOLFBOOT_ENABLE_PK_RSAPSS3072) || \ + defined(WOLFBOOT_ENABLE_PK_RSAPSS4096) || \ (defined(WOLFCRYPT_SECURE_MODE) && (!defined(PKCS11_SMALL))) /* RSA blinding protects RSA private-key operations against timing @@ -248,11 +307,9 @@ extern int tolower(int c); # 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_ENABLE_PK_RSAPSS2048) || \ + defined(WOLFBOOT_ENABLE_PK_RSAPSS3072) || \ + defined(WOLFBOOT_ENABLE_PK_RSAPSS4096) # define WC_RSA_PSS # endif # if !defined(WOLFBOOT_TPM) && !defined(WOLFCRYPT_SECURE_MODE) && \ @@ -262,7 +319,11 @@ extern int tolower(int c); # define WOLFSSL_RSA_VERIFY_INLINE # define WOLFSSL_RSA_VERIFY_ONLY # define WOLFSSL_RSA_PUBLIC_ONLY -# if !defined(WC_RSA_PSS) + /* With WC_NO_RSA_OAEP set, wolfssl settings.h may strip + * ConstantCompare (WOLFSSL_NO_CONST_CMP), which ed25519/ed448 + * verify still needs. Keep OAEP when an Ed algo is compiled in. */ +# if !defined(WC_RSA_PSS) && !defined(WOLFBOOT_ENABLE_PK_ED25519) && \ + !defined(WOLFBOOT_ENABLE_PK_ED448) # define WC_NO_RSA_OAEP # endif # define NO_RSA_BOUNDS_CHECK @@ -273,46 +334,59 @@ 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) -# define FP_MAX_BITS (2048 * 2) -# define SP_INT_BITS 2048 -# define WOLFSSL_SP_NO_3072 -# define WOLFSSL_SP_NO_4096 -# define WOLFSSL_SP_2048 -# define RSA_MIN_SIZE 2048 -# define RSA_MAX_SIZE 2048 + /* Key sizes in use (secure mode serves all of them) */ +# if defined(WOLFBOOT_ENABLE_PK_RSA2048) || \ + defined(WOLFBOOT_ENABLE_PK_RSAPSS2048) || defined(WOLFCRYPT_SECURE_MODE) +# define WOLFBOOT_ENABLE_RSA2048_SIZE # endif -# if defined(WOLFBOOT_SIGN_RSA3072) || defined(WOLFBOOT_SIGN_SECONDARY_RSA3072) || \ - defined(WOLFBOOT_SIGN_RSAPSS3072) || defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) -# define FP_MAX_BITS (3072 * 2) -# define SP_INT_BITS 3072 -# define WOLFSSL_SP_NO_2048 -# define WOLFSSL_SP_NO_4096 -# define WOLFSSL_SP_3072 -# define RSA_MIN_SIZE 3072 -# define RSA_MAX_SIZE 3072 +# if defined(WOLFBOOT_ENABLE_PK_RSA3072) || \ + defined(WOLFBOOT_ENABLE_PK_RSAPSS3072) || defined(WOLFCRYPT_SECURE_MODE) +# define WOLFBOOT_ENABLE_RSA3072_SIZE # endif - -# if defined(WOLFBOOT_SIGN_RSA4096) || defined(WOLFBOOT_SIGN_SECONDARY_RSA4096) || \ - defined(WOLFBOOT_SIGN_RSAPSS4096) || defined(WOLFBOOT_SIGN_SECONDARY_RSAPSS4096) -# define FP_MAX_BITS (4096 * 2) -# define SP_INT_BITS 4096 +# if defined(WOLFBOOT_ENABLE_PK_RSA4096) || \ + defined(WOLFBOOT_ENABLE_PK_RSAPSS4096) || defined(WOLFCRYPT_SECURE_MODE) +# define WOLFBOOT_ENABLE_RSA4096_SIZE +# endif + /* SP code for each selected size; strip the others */ +# ifdef WOLFBOOT_ENABLE_RSA2048_SIZE +# define WOLFSSL_SP_2048 +# else # define WOLFSSL_SP_NO_2048 +# endif +# ifdef WOLFBOOT_ENABLE_RSA3072_SIZE +# define WOLFSSL_SP_3072 +# else # define WOLFSSL_SP_NO_3072 +# endif +# ifdef WOLFBOOT_ENABLE_RSA4096_SIZE # define WOLFSSL_SP_4096 -# define RSA_MIN_SIZE 4096 -# define RSA_MAX_SIZE 4096 +# else +# define WOLFSSL_SP_NO_4096 # endif -# ifdef WOLFCRYPT_SECURE_MODE + /* Math sized for the largest key; any RSA key needs more than any + * ECC curve enabled above */ +# ifdef FP_MAX_BITS # undef FP_MAX_BITS +# endif +# if defined(WOLFBOOT_ENABLE_RSA4096_SIZE) # define FP_MAX_BITS (4096 * 2) # define SP_INT_BITS 4096 -# define WOLFSSL_SP_2048 -# define WOLFSSL_SP_3072 -# define WOLFSSL_SP_4096 -# define RSA_MIN_SIZE 2048 # define RSA_MAX_SIZE 4096 +# elif defined(WOLFBOOT_ENABLE_RSA3072_SIZE) +# define FP_MAX_BITS (3072 * 2) +# define SP_INT_BITS 3072 +# define RSA_MAX_SIZE 3072 +# elif defined(WOLFBOOT_ENABLE_RSA2048_SIZE) +# define FP_MAX_BITS (2048 * 2) +# define SP_INT_BITS 2048 +# define RSA_MAX_SIZE 2048 +# endif +# if defined(WOLFBOOT_ENABLE_RSA2048_SIZE) +# define RSA_MIN_SIZE 2048 +# elif defined(WOLFBOOT_ENABLE_RSA3072_SIZE) +# define RSA_MIN_SIZE 3072 +# elif defined(WOLFBOOT_ENABLE_RSA4096_SIZE) +# define RSA_MIN_SIZE 4096 # endif #else # define NO_RSA @@ -343,22 +417,16 @@ extern int tolower(int c); # define WOLFSSL_SP_NO_DYN_STACK #endif /* WOLFBOOT_SIGN_ML_DSA || WOLFBOOT_SIGN_SECONDARY_ML_DSA */ -#ifdef WOLFBOOT_HASH_SHA3_384 +#ifdef WOLFBOOT_ENABLE_HASH_SHA3 # define WOLFSSL_SHA3 -# if defined(NO_RSA) && !defined(WOLFBOOT_TPM) && \ - !defined(WOLFCRYPT_SECURE_MODE) && \ - !defined(WOLFCRYPT_TEST) && !defined(WOLFCRYPT_BENCHMARK) -# define NO_SHA256 -# endif #endif -#ifdef WOLFBOOT_HASH_SHA384 +#ifdef WOLFBOOT_ENABLE_HASH_SHA384 # define WOLFSSL_SHA384 -# if defined(NO_RSA) && !defined(WOLFBOOT_TPM) && \ - !defined(WOLFCRYPT_SECURE_MODE) && \ - !defined(WOLFCRYPT_TEST) && !defined(WOLFCRYPT_BENCHMARK) -# define NO_SHA256 -# endif +#endif + +/* SHA-384 is computed through the SHA-512 core */ +#if defined(WOLFBOOT_ENABLE_HASH_SHA384) || defined(WOLFBOOT_ENABLE_HASH_SHA512) # ifndef WOLFSSL_SHA512 # define WOLFSSL_SHA512 # define WOLFSSL_NOSHA512_224 @@ -366,6 +434,14 @@ extern int tolower(int c); # endif #endif +/* Drop SHA-256 when neither the image hash nor any other user needs it */ +#if (defined(WOLFBOOT_HASH_SHA384) || defined(WOLFBOOT_HASH_SHA3_384)) && \ + !defined(WOLFBOOT_ENABLE_HASH_SHA256) && defined(NO_RSA) && \ + !defined(WOLFBOOT_TPM) && !defined(WOLFCRYPT_SECURE_MODE) && \ + !defined(WOLFCRYPT_TEST) && !defined(WOLFCRYPT_BENCHMARK) +# define NO_SHA256 +#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/options.mk b/options.mk index b6adb4a588..35052b94c2 100644 --- a/options.mk +++ b/options.mk @@ -1618,26 +1618,63 @@ ifneq ($(CERT_CHAIN_VERIFY),) CFLAGS += '-DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST=$(WOLFHSM_NVM_ROOT_CA_LIST)' endif - # User-provided cert chain takes precedence + # User-provided cert chain takes precedence. Declare the chain's + # algorithms via AUX_PK_ALGOS/AUX_HASH_ALGOS so the verifier has them. ifneq ($(USER_CERT_CHAIN),) CERT_CHAIN_FILE = $(USER_CERT_CHAIN) else # Auto-generate dummy cert chain (when USER_CERT_CHAIN not provided) CERT_CHAIN_FILE = test-dummy-ca/raw-chain.der - # Set appropriate cert gen algo based on signature algorithm + # The leaf cert wraps the signing key, so its algo follows SIGN ifeq ($(SIGN),ECC256) - CERT_CHAIN_GEN_ALGO+=ecc256 + CERT_CHAIN_GEN_LEAF_ALGO:=ecc256 + endif + ifeq ($(SIGN),ECC384) + CERT_CHAIN_GEN_LEAF_ALGO:=ecc384 + endif + ifeq ($(SIGN),ECC521) + CERT_CHAIN_GEN_LEAF_ALGO:=ecc521 endif ifeq ($(SIGN),RSA2048) - CERT_CHAIN_GEN_ALGO+=rsa2048 + CERT_CHAIN_GEN_LEAF_ALGO:=rsa2048 + endif + ifeq ($(SIGN),RSA3072) + CERT_CHAIN_GEN_LEAF_ALGO:=rsa3072 endif ifeq ($(SIGN),RSA4096) - CERT_CHAIN_GEN_ALGO+=rsa4096 - # Reasonably large default - CFLAGS += -DWOLFHSM_CFG_MAX_CERT_SIZE=4096 + CERT_CHAIN_GEN_LEAF_ALGO:=rsa4096 + endif + ifeq ($(SIGN),RSAPSS2048) + CERT_CHAIN_GEN_LEAF_ALGO:=rsa2048 + endif + ifeq ($(SIGN),RSAPSS3072) + CERT_CHAIN_GEN_LEAF_ALGO:=rsa3072 + endif + ifeq ($(SIGN),RSAPSS4096) + CERT_CHAIN_GEN_LEAF_ALGO:=rsa4096 + endif + ifeq ($(CERT_CHAIN_GEN_LEAF_ALGO),) + $(error dummy cert chain generation does not support SIGN=$(SIGN); \ + provide USER_CERT_CHAIN instead) + endif + + # Root/intermediate key algo and cert signature hash are configurable + CERT_CHAIN_GEN_CA_ALGO?=$(CERT_CHAIN_GEN_LEAF_ALGO) + CERT_CHAIN_GEN_CA_HASH?=sha256 + + # Compile in the CA's algorithms so the chain verifies in boot + ifneq ($(CERT_CHAIN_GEN_CA_ALGO),$(CERT_CHAIN_GEN_LEAF_ALGO)) + AUX_PK_ALGOS:=$(AUX_PK_ALGOS),$(CERT_CHAIN_GEN_CA_ALGO) + endif + ifneq ($(CERT_CHAIN_GEN_CA_HASH),sha256) + AUX_HASH_ALGOS:=$(AUX_HASH_ALGOS),$(CERT_CHAIN_GEN_CA_HASH) endif endif + # Reasonably large default cert buffer when RSA4096 is in the chain + ifneq (,$(filter rsa4096,$(CERT_CHAIN_GEN_CA_ALGO) $(CERT_CHAIN_GEN_LEAF_ALGO))) + CFLAGS += -DWOLFHSM_CFG_MAX_CERT_SIZE=4096 + endif SIGN_OPTIONS += --cert-chain $(CERT_CHAIN_FILE) endif @@ -1681,3 +1718,119 @@ endif ifeq ($(TZEN),1) CFLAGS+=-DTZEN endif + +# Auxiliary algorithms: compile extra wolfCrypt code beyond what SIGN/HASH +# selects, for features that need it (cert-chain verification, TPM, ...). +# Auxiliary algorithms are never used to verify image signatures. +# AUX_PK_ALGOS / AUX_HASH_ALGOS take comma-separated, case-insensitive +# entries, e.g. AUX_PK_ALGOS=rsa2048,ecc384 AUX_HASH_ALGOS=sha384. +# This section must stay below anything that appends to the two lists +# (e.g. the cert-chain block above). +AUX_PK_ALGOS?= +AUX_HASH_ALGOS?= +AUX_PK_LIST:=$(sort $(shell echo $(AUX_PK_ALGOS) | tr 'A-Z,' 'a-z ')) +AUX_HASH_LIST:=$(sort $(shell echo $(AUX_HASH_ALGOS) | tr 'A-Z,' 'a-z ')) +SIGN_TOKENS_LC:=$(shell echo $(SIGN) $(SIGN_SECONDARY) | tr 'A-Z' 'a-z') + +# TPM needs ECC or RSA for the SRK and parameter encryption +ifeq ($(WOLFTPM),1) + ifeq (,$(filter ecc% rsa%,$(SIGN_TOKENS_LC) $(AUX_PK_LIST))) + AUX_PK_LIST+=ecc256 + endif +endif + +ifneq (,$(strip $(AUX_PK_LIST) $(AUX_HASH_LIST))) + ifeq ($(WOLFBOOT_SMALL_STACK),1) + $(error auxiliary algorithms not supported with WOLFBOOT_SMALL_STACK: \ + the static pools in src/xmalloc.c only cover the primary SIGN algorithm) + endif +endif + +AUX_PK_VALID:=ecc256 ecc384 ecc521 rsa2048 rsa3072 rsa4096 \ + rsapss2048 rsapss3072 rsapss4096 ed25519 ed448 +AUX_HASH_VALID:=sha256 sha384 sha512 sha3 +ifneq (,$(filter-out $(AUX_PK_VALID),$(AUX_PK_LIST))) + $(error invalid AUX_PK_ALGOS entries "$(filter-out $(AUX_PK_VALID),$(AUX_PK_LIST))". Valid: $(AUX_PK_VALID)) +endif +ifneq (,$(filter-out $(AUX_HASH_VALID),$(AUX_HASH_LIST))) + $(error invalid AUX_HASH_ALGOS entries "$(filter-out $(AUX_HASH_VALID),$(AUX_HASH_LIST))". Valid: $(AUX_HASH_VALID)) +endif + +# Entries matching SIGN/SIGN_SECONDARY are already compiled in +AUX_PK_EFFECTIVE:=$(filter-out $(SIGN_TOKENS_LC),$(AUX_PK_LIST)) + +AUX_WOLFCRYPT_OBJS:= +ifneq (,$(filter ecc256,$(AUX_PK_EFFECTIVE))) + CFLAGS+=-DWOLFBOOT_AUX_PK_ECC256 + AUX_WOLFCRYPT_OBJS+=$(ECC_OBJS) $(MATH_OBJS) +endif +ifneq (,$(filter ecc384,$(AUX_PK_EFFECTIVE))) + CFLAGS+=-DWOLFBOOT_AUX_PK_ECC384 + AUX_WOLFCRYPT_OBJS+=$(ECC_OBJS) $(MATH_OBJS) +endif +ifneq (,$(filter ecc521,$(AUX_PK_EFFECTIVE))) + CFLAGS+=-DWOLFBOOT_AUX_PK_ECC521 + AUX_WOLFCRYPT_OBJS+=$(ECC_OBJS) $(MATH_OBJS) +endif +ifneq (,$(filter rsa2048,$(AUX_PK_EFFECTIVE))) + CFLAGS+=-DWOLFBOOT_AUX_PK_RSA2048 + AUX_WOLFCRYPT_OBJS+=$(RSA_OBJS) $(MATH_OBJS) +endif +ifneq (,$(filter rsa3072,$(AUX_PK_EFFECTIVE))) + CFLAGS+=-DWOLFBOOT_AUX_PK_RSA3072 + AUX_WOLFCRYPT_OBJS+=$(RSA_OBJS) $(MATH_OBJS) +endif +ifneq (,$(filter rsa4096,$(AUX_PK_EFFECTIVE))) + CFLAGS+=-DWOLFBOOT_AUX_PK_RSA4096 + AUX_WOLFCRYPT_OBJS+=$(RSA_OBJS) $(MATH_OBJS) +endif +ifneq (,$(filter rsapss2048,$(AUX_PK_EFFECTIVE))) + CFLAGS+=-DWOLFBOOT_AUX_PK_RSAPSS2048 + AUX_WOLFCRYPT_OBJS+=$(RSA_OBJS) $(MATH_OBJS) +endif +ifneq (,$(filter rsapss3072,$(AUX_PK_EFFECTIVE))) + CFLAGS+=-DWOLFBOOT_AUX_PK_RSAPSS3072 + AUX_WOLFCRYPT_OBJS+=$(RSA_OBJS) $(MATH_OBJS) +endif +ifneq (,$(filter rsapss4096,$(AUX_PK_EFFECTIVE))) + CFLAGS+=-DWOLFBOOT_AUX_PK_RSAPSS4096 + AUX_WOLFCRYPT_OBJS+=$(RSA_OBJS) $(MATH_OBJS) +endif +ifneq (,$(filter ed25519,$(AUX_PK_EFFECTIVE))) + CFLAGS+=-DWOLFBOOT_AUX_PK_ED25519 + AUX_WOLFCRYPT_OBJS+=$(ED25519_OBJS) +endif +ifneq (,$(filter ed448,$(AUX_PK_EFFECTIVE))) + CFLAGS+=-DWOLFBOOT_AUX_PK_ED448 + AUX_WOLFCRYPT_OBJS+=$(ED448_OBJS) + AUX_WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha3.o + AUX_WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha512.o +endif + +# ED448 defines WOLFSSL_SHA512 in user_settings.h but ED448_OBJS carries no +# sha512.o; make sure it gets linked (deduplicated below) +ifneq (,$(filter ED448,$(SIGN) $(SIGN_SECONDARY))) + AUX_WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha512.o +endif + +ifneq (,$(filter sha256,$(AUX_HASH_LIST))) + CFLAGS+=-DWOLFBOOT_AUX_HASH_SHA256 +endif +ifneq (,$(filter sha384,$(AUX_HASH_LIST))) + CFLAGS+=-DWOLFBOOT_AUX_HASH_SHA384 + AUX_WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha512.o +endif +ifneq (,$(filter sha512,$(AUX_HASH_LIST))) + CFLAGS+=-DWOLFBOOT_AUX_HASH_SHA512 + AUX_WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha512.o +endif +ifneq (,$(filter sha3,$(AUX_HASH_LIST))) + CFLAGS+=-DWOLFBOOT_AUX_HASH_SHA3 + AUX_WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha3.o +endif + +# Append only objects not already selected by SIGN/HASH/features. +# Snapshot with := first: WOLFCRYPT_OBJS is a recursive variable in some +# includers (test-app), where a self-referencing += would not terminate. +AUX_WOLFCRYPT_OBJS_NEW:=$(filter-out $(WOLFCRYPT_OBJS),$(sort $(AUX_WOLFCRYPT_OBJS))) +WOLFCRYPT_OBJS+=$(AUX_WOLFCRYPT_OBJS_NEW) diff --git a/tools/config.mk b/tools/config.mk index 4f00a91eca..d40627063b 100644 --- a/tools/config.mk +++ b/tools/config.mk @@ -138,4 +138,8 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO WOLFHSM_CLIENT \ WOLFHSM_CLIENT_ID \ WOLFHSM_NVM_ROOT_CA_LIST \ + AUX_PK_ALGOS \ + AUX_HASH_ALGOS \ + CERT_CHAIN_GEN_CA_ALGO \ + CERT_CHAIN_GEN_CA_HASH \ ENCRYPT_CACHE diff --git a/tools/scripts/sim-gen-dummy-chain.sh b/tools/scripts/sim-gen-dummy-chain.sh index 3688f3190d..f70533b709 100755 --- a/tools/scripts/sim-gen-dummy-chain.sh +++ b/tools/scripts/sim-gen-dummy-chain.sh @@ -1,61 +1,83 @@ #!/bin/bash -# Certificate Chain Generation Script (ECC P256 or RSA) +# Certificate Chain Generation Script (ECC 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 +# The CA (root+intermediate) key algo, the CA cert signature hash, and the +# leaf key algo can be chosen independently. set -e # Exit on any error -# Default output directory and algorithm +# Defaults OUTPUT_DIR="test-dummy-ca" -ALGO="ecc256" # Default to ECC P-256 keys +LEGACY_ALGO="" # --algo: sets both CA and leaf (backward compatible) +CA_ALGO="" +LEAF_ALGO="" +CA_HASH="" + +VALID_ALGOS="ecc256 ecc384 ecc521 rsa2048 rsa3072 rsa4096" +VALID_HASHES="sha256 sha384 sha512" + +validate_choice() { + local kind=$1 value=$2 valid=$3 + for v in $valid; do + [[ "$value" == "$v" ]] && return 0 + done + echo "Invalid $kind: $value. Use one of: $valid" + exit 1 +} -# Helper functions for key operations +# Helper functions for key operations (first argument selects the algorithm) 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 + local algo=$1 + local output_file=$2 + + case "$algo" in + ecc256) openssl ecparam -genkey -name prime256v1 -noout -out "$output_file" ;; + ecc384) openssl ecparam -genkey -name secp384r1 -noout -out "$output_file" ;; + ecc521) openssl ecparam -genkey -name secp521r1 -noout -out "$output_file" ;; + rsa2048) openssl genrsa -out "$output_file" 2048 ;; + rsa3072) openssl genrsa -out "$output_file" 3072 ;; + rsa4096) openssl genrsa -out "$output_file" 4096 ;; + esac } convert_key_to_der() { - local input_file=$1 - local output_file=$2 + local algo=$1 + local input_file=$2 + local output_file=$3 - if [[ "$ALGO" == "ecc256" ]]; then + if [[ "$algo" == ecc* ]]; then openssl ec -in "$input_file" -outform DER -out "$output_file" - elif [[ "$ALGO" == "rsa2048" || "$ALGO" == "rsa4096" ]]; then + else openssl rsa -in "$input_file" -outform DER -out "$output_file" fi } 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) openssl x509 -in "$cert_file" -pubkey -noout > "$pubkey_pem" # Convert public key to DER format - if [[ "$ALGO" == "ecc256" ]]; then + if [[ "$algo" == ecc* ]]; then openssl ec -pubin -in "$pubkey_pem" -outform DER -out "$pubkey_der" - elif [[ "$ALGO" == "rsa2048" || "$ALGO" == "rsa4096" ]]; then + else openssl rsa -pubin -in "$pubkey_pem" -outform DER -out "$pubkey_der" fi } validate_key_format() { - local key_file=$1 + local algo=$1 + local key_file=$2 - if [[ "$ALGO" == "ecc256" ]]; then + if [[ "$algo" == ecc* ]]; then openssl ec -in "$key_file" -noout - elif [[ "$ALGO" == "rsa2048" || "$ALGO" == "rsa4096" ]]; then + else openssl rsa -in "$key_file" -noout fi } @@ -73,21 +95,40 @@ 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'" - exit 1 - fi + LEGACY_ALGO="$2" + validate_choice "algorithm" "$LEGACY_ALGO" "$VALID_ALGOS" + shift 2 + ;; + --ca-algo) + CA_ALGO="$2" + validate_choice "CA algorithm" "$CA_ALGO" "$VALID_ALGOS" + shift 2 + ;; + --leaf-algo) + LEAF_ALGO="$2" + validate_choice "leaf algorithm" "$LEAF_ALGO" "$VALID_ALGOS" + shift 2 + ;; + --ca-hash) + CA_HASH="$2" + validate_choice "CA hash" "$CA_HASH" "$VALID_HASHES" shift 2 ;; *) echo "Unknown option: $1" - echo "Usage: $0 [--leaf ] [--outdir ] [--algo ]" + echo "Usage: $0 [--leaf ] [--outdir ]" \ + "[--algo ] [--ca-algo ] [--leaf-algo ] [--ca-hash ]" + echo " : $VALID_ALGOS" exit 1 ;; esac done +# --algo sets both unless overridden; default ecc256 +CA_ALGO="${CA_ALGO:-${LEGACY_ALGO:-ecc256}}" +LEAF_ALGO="${LEAF_ALGO:-${LEGACY_ALGO:-ecc256}}" +CA_HASH="${CA_HASH:-sha256}" + # 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 +141,14 @@ mkdir -p ${OUTPUT_DIR}/temp ################## # GENERATE CHAIN ################## -echo "Generating Certificate Chain using $ALGO..." +echo "Generating Certificate Chain (CA: $CA_ALGO/$CA_HASH, leaf: $LEAF_ALGO)..." # Step 1: Generate Root key and certificate 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 \ -key ${OUTPUT_DIR}/temp/root.key.pem \ -out ${OUTPUT_DIR}/temp/root.crt.pem \ -subj "$ROOT_SUBJECT" \ @@ -115,12 +156,12 @@ 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 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 \ -key ${OUTPUT_DIR}/temp/intermediate.key.pem \ @@ -128,7 +169,7 @@ openssl req -new -sha256 \ -subj "$INTERMEDIATE_SUBJECT" # Step 3: Sign Intermediate certificate with Root -openssl x509 -req -days 1825 -sha256 \ +openssl x509 -req -days 1825 -$CA_HASH \ -in ${OUTPUT_DIR}/temp/intermediate.csr \ -out ${OUTPUT_DIR}/temp/intermediate.crt.pem \ -CA ${OUTPUT_DIR}/temp/root.crt.pem \ @@ -137,19 +178,19 @@ 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) 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" + generate_private_key "$LEAF_ALGO" "${OUTPUT_DIR}/temp/leaf.key.pem" else echo "Using provided leaf private key: $LEAF_KEY_FILE" 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" + validate_key_format "$LEAF_ALGO" "${OUTPUT_DIR}/temp/leaf.key.pem" fi # Create CSR for leaf certificate @@ -158,8 +199,8 @@ openssl req -new -sha256 \ -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 (CA key, CA hash) +openssl x509 -req -days 365 -$CA_HASH \ -in ${OUTPUT_DIR}/temp/leaf.csr \ -out ${OUTPUT_DIR}/temp/leaf.crt.pem \ -CA ${OUTPUT_DIR}/temp/intermediate.crt.pem \ @@ -168,12 +209,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 +291,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: $CA_ALGO/$CA_HASH, leaf: $LEAF_ALGO):" 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"