[PATCH RFC] fit: allow to specify RSAPSS padding salt length magic value
Quentin Schulz
quentin.schulz at cherry.de
Thu May 15 16:56:39 CEST 2025
Hi all,
Gentle ping, anything to say/suggest?
Cheers,
Quentin
On 4/11/25 6:32 PM, Quentin Schulz wrote:
> From: Quentin Schulz <quentin.schulz at cherry.de>
>
> Before commit 1b99c15d73c1 ("lib: rsa: Set conventional salt length
> RSA-PSS parameter"), the salt length for PSS padding was openSSL's
> default. Prior to openSSL 3.1, this was the maximum allowed value ("max"
> when passed from CLI or RSA_PSS_SALTLEN_MAX (-3) if from library).[1]
> Starting from 3.1, the default is now "maximized up to the digest
> length" ("auto-digestmax" from CLI or RSA_PSS_SALTLEN_AUTO_DIGEST_MAX
> (-4) from library).[2]
>
> After commit 1b99c15d73c1 ("lib: rsa: Set conventional salt length
> RSA-PSS parameter"), the salt length has been forced to the digest
> length, but manually instead of using "digest" from CLI or
> RSA_PSS_SALTLEN_DIGEST from lib.
>
> Before commit 81eff51047e2 ("lib: rsa: Update function
> padding_pss_verify (any-salt)"), verifying a signature with PSS padding
> could only be done when the padding was using the max value for the salt
> length. While that is now history and the code should now detect the
> appropriate length when verifying, this lack of support in older
> versions bring some issue. Indeed, U-Boot is typically split in two
> binaries (at least on Rockchip platforms), one for U-Boot FIT (with
> U-Boot proper, TF-A, TEE, etc..) which can be signed and one for the
> stage(s) before U-Boot proper, who's in charge of verifying the FIT
> signature. It would be possible to update only U-Boot FIT and not the
> other prior stages binary in which case the device would be bricked if
> it has a pre-81eff51047e2 prior stages with a U-Boot FIT signed from
> post-1b99c15d73c1 or from pre-1b99c15d73c1 with openSSL >= 3.1.
>
> This introduces a "padding-saltlen" string property to the signature
> node of a FIT node which expects the CLI name of the openSSL option for
> setting the RSAPSS padding salt length, it can be:
> - "max"
> - "digest"
> - "auto" ("max" alias, but U-Boot internals pass "auto" to openSSL)
> - "auto-digestmax" (only available on openSSL >= 3.1)
>
> If it is missing, "digest" is assumed, to match current behavior.
>
> Note that while openSSL allows to pass the length as number, the string
> magic values are represented as negative numbers. FDT only allowing
> u32/u64 for numbers, we cannot represent those negative numbers in the
> same property as the magic string values. Hence the choice to go with a
> string property.
>
> [1] https://docs.openssl.org/3.0/man3/EVP_PKEY_CTX_ctrl/#rsa-parameters
> [2] https://docs.openssl.org/3.4/man3/EVP_PKEY_CTX_ctrl/#rsa-parameters
>
> Signed-off-by: Quentin Schulz <quentin.schulz at cherry.de>
> ---
> How to test:
> - generate keys and certs at the root of U-Boot's source directory:
> - openssl genpkey -algorithm RSA -out dev.key \
> -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537
> - openssl req -batch -new -x509 -key dev.key -out dev.crt
> - apply the following diff for generating signed U-Boot FIT + adding the
> pubkey to U-Boot SPL + making signature verification required:
>
> diff --git a/arch/arm/dts/rockchip-u-boot.dtsi b/arch/arm/dts/rockchip-u-boot.dtsi
> index c8c928c7e50..e5fd8c73602 100644
> --- a/arch/arm/dts/rockchip-u-boot.dtsi
> +++ b/arch/arm/dts/rockchip-u-boot.dtsi
> @@ -29,7 +29,14 @@
> u-boot-tpl {
> };
> #endif
> - u-boot-spl {
> + section {
> + u-boot-spl-nodtb {
> + };
> + u-boot-spl-pubkey-dtb {
> + algo = "sha256,rsa2048";
> + required = "conf";
> + key-name-hint = "dev";
> + };
> };
> };
>
> @@ -45,6 +52,7 @@
> filename = "u-boot.itb";
> fit,external-offset = <CONFIG_FIT_EXTERNAL_OFFSET>;
> fit,align = <512>;
> + fit,sign;
> offset = <CONFIG_SPL_PAD_TO>;
> images {
> u-boot {
> @@ -162,6 +170,13 @@
> fit,firmware = "op-tee", "u-boot";
> #endif
> fit,loadables;
> +
> + signature {
> + algo = "sha256,rsa2048";
> + key-name-hint = "dev";
> + padding = "pss";
> + sign-images = "fdt", "loadables", "firmware";
> + };
> };
> };
> };
>
> You need RSA and RSAPSS support in SPL for this to work (it'll compile
> just fine but always fail to verify).
>
> It'll successfully boot.
>
> Now revert commit 81eff51047e2 ("lib: rsa: Update function
> padding_pss_verify (any-salt)") and regenerate the images.
>
> U-Boot SPL will fail to verify U-Boot FIT signature and refuse to boot.
>
> Now, add `padding-saltlen = "max";` to the signature node and regenerate
> the images, U-Boot SPL will now be able to verify U-Boot FIT signature.
>
> Note that I have no security background and I essentially just made sure
> it compiles and boots (or refuses to boot).
>
> This is marked as RFC because we would need to add the property to
> https://fitspec.osfw.foundation/#configuration-signature-nodes
> and
> https://fitspec.osfw.foundation/#image-signature-nodes
> I assume. Don't want to suggest something to the FIT spec if it's anyway
> going to be denied in U-Boot :)
>
> Note that I actually need a patch like this (for now I just hardcoded
> the salt length to use MAX instead of digest length) for one of our
> products. I don't mind keeping it downstream but I felt like this was a
> semi-reasonable approach to making this configurable.
>
> What do you think?
> ---
> include/image.h | 1 +
> lib/rsa/rsa-sign.c | 34 +++++++++++++++++++++++++++-------
> tools/image-host.c | 3 +++
> 3 files changed, 31 insertions(+), 7 deletions(-)
>
> diff --git a/include/image.h b/include/image.h
> index c1db8383459c9d7ce1ecc318ccccb4627a7066e5..785852a92f838b7b89bede1c6e6c9a346a9efd1d 100644
> --- a/include/image.h
> +++ b/include/image.h
> @@ -1511,6 +1511,7 @@ struct image_sign_info {
> int required_keynode; /* Node offset of key to use: -1=any */
> const char *require_keys; /* Value for 'required' property */
> const char *engine_id; /* Engine to use for signing */
> + const char *saltlen_name; /* OpenSSL RSA PSS salt length magic value */
> /*
> * Note: the following two fields are always valid even w/o
> * RSA_VERIFY_WITH_PKEY in order to make sure this structure is
> diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c
> index fa9e143b4ca8cbc50297acdfbb4ccb39cb160f84..10b25094a2fada08eb37c38a5d618b6e07972139 100644
> --- a/lib/rsa/rsa-sign.c
> +++ b/lib/rsa/rsa-sign.c
> @@ -384,7 +384,7 @@ static void rsa_engine_remove(ENGINE *e)
> static int rsa_sign_with_key(EVP_PKEY *pkey, struct padding_algo *padding_algo,
> struct checksum_algo *checksum_algo,
> const struct image_region region[], int region_count,
> - uint8_t **sigp, uint *sig_size)
> + uint8_t **sigp, uint *sig_size, const char *saltlen_name)
> {
> EVP_PKEY_CTX *ckey;
> EVP_MD_CTX *context;
> @@ -423,17 +423,37 @@ static int rsa_sign_with_key(EVP_PKEY *pkey, struct padding_algo *padding_algo,
>
> if (CONFIG_IS_ENABLED(FIT_RSASSA_PSS) && padding_algo &&
> !strcmp(padding_algo->name, "pss")) {
> + int saltlen;
> +
> if (EVP_PKEY_CTX_set_rsa_padding(ckey,
> RSA_PKCS1_PSS_PADDING) <= 0) {
> ret = rsa_err("Signer padding setup failed");
> goto err_sign;
> }
>
> - /* Per RFC 3447 (and convention) the Typical salt length is the
> - * length of the output of the digest algorithm.
> - */
> - if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ckey,
> - checksum_algo->checksum_len) <= 0) {
> + if (saltlen_name && !strcmp(saltlen_name, "max")) {
> + saltlen = RSA_PSS_SALTLEN_MAX;
> + } else if (saltlen_name && !strcmp(saltlen_name, "auto")) {
> + saltlen = RSA_PSS_SALTLEN_AUTO;
> +#if OPENSSL_VERSION_NUMBER >= 0x31000000L
> + } else if (saltlen_name && !strcmp(saltlen_name, "auto-digestmax")) {
> + saltlen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX;
> +#endif
> + } else if (saltlen_name && !strcmp(saltlen_name, "digest")) {
> + saltlen = RSA_PSS_SALTLEN_DIGEST;
> + } else if (saltlen_name) {
> + fprintf(stderr, "Invalid salt length '%s'\n", saltlen_name);
> + rsa_err("Invalid salt length");
> + goto err_sign;
> + } else {
> + /* Per RFC 3447 (and convention) the Typical salt length is the
> + * length of the output of the digest algorithm.
> + */
> + debug("padding saltlen missing, assuming \"digest\"\n");
> + saltlen = RSA_PSS_SALTLEN_DIGEST;
> + }
> +
> + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ckey, saltlen) <= 0) {
> ret = rsa_err("Signer salt length setup failed");
> goto err_sign;
> }
> @@ -491,7 +511,7 @@ int rsa_sign(struct image_sign_info *info,
> if (ret)
> goto err_priv;
> ret = rsa_sign_with_key(pkey, info->padding, info->checksum, region,
> - region_count, sigp, sig_len);
> + region_count, sigp, sig_len, info->saltlen_name);
> if (ret)
> goto err_sign;
>
> diff --git a/tools/image-host.c b/tools/image-host.c
> index a9b8690276386aa21983612c6a306b7897a0af26..63055f2044ac5c320c52dc87e59b275d4bc79e4a 100644
> --- a/tools/image-host.c
> +++ b/tools/image-host.c
> @@ -175,6 +175,7 @@ static int fit_image_setup_sig(struct image_sign_info *info,
> {
> const char *node_name;
> const char *padding_name;
> + const char *saltlen_name;
>
> node_name = fit_get_name(fit, noffset, NULL);
> if (!algo_name) {
> @@ -187,6 +188,7 @@ static int fit_image_setup_sig(struct image_sign_info *info,
> }
>
> padding_name = fdt_getprop(fit, noffset, "padding", NULL);
> + saltlen_name = fdt_getprop(fit, noffset, "padding-saltlen", NULL);
>
> memset(info, '\0', sizeof(*info));
> info->keydir = keydir;
> @@ -198,6 +200,7 @@ static int fit_image_setup_sig(struct image_sign_info *info,
> info->checksum = image_get_checksum_algo(algo_name);
> info->crypto = image_get_crypto_algo(algo_name);
> info->padding = image_get_padding_algo(padding_name);
> + info->saltlen_name = saltlen_name;
> info->require_keys = require_keys;
> info->engine_id = engine_id;
> if (!info->checksum || !info->crypto) {
>
> ---
> base-commit: cb7555e93075114fe4af0adb806877ac4d4ef80d
> change-id: 20250411-rsapss-saltlen-b8d7a676565a
>
> Best regards,
More information about the U-Boot
mailing list