[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