All of lore.kernel.org
 help / color / mirror / Atom feed
From: Nicolai Stange <nstange@suse.de>
To: Herbert Xu <herbert@gondor.apana.org.au>
Cc: Nicolai Stange <nstange@suse.de>,
	Stephan Mueller <smueller@chronox.de>,
	"David S. Miller" <davem@davemloft.net>,
	Hannes Reinecke <hare@suse.de>, Torsten Duwe <duwe@suse.de>,
	Zaibo Xu <xuzaibo@huawei.com>,
	Giovanni Cabiddu <giovanni.cabiddu@intel.com>,
	David Howells <dhowells@redhat.com>,
	Jarkko Sakkinen <jarkko@kernel.org>,
	linux-crypto@vger.kernel.org, linux-kernel@vger.kernel.org,
	qat-linux@intel.com, keyrings@vger.kernel.org, simo@redhat.com,
	Eric Biggers <ebiggers@kernel.org>, Petr Vorel <pvorel@suse.cz>
Subject: Re: [v2 PATCH] crypto: api - Disallow sha1 in FIPS-mode while allowing hmac(sha1)
Date: Fri, 14 Jan 2022 10:09:02 +0100	[thread overview]
Message-ID: <87k0f2hefl.fsf@suse.de> (raw)
In-Reply-To: <YeEVSaMEVJb3cQkq@gondor.apana.org.au> (Herbert Xu's message of "Fri, 14 Jan 2022 17:16:41 +1100")

Hi Herbert,

many thanks for this, I think this approach can be applied as-is to the
ffdheXYZ(dh) situation. I have some questions/comments inline.

Herbert Xu <herbert@gondor.apana.org.au> writes:

> On Tue, Jan 11, 2022 at 09:34:19PM +1100, Herbert Xu wrote:
>>
>> You're right.  The real issue is that any algorithm with no tests
>> at all is allowed in FIPS mode.  That's clearly suboptimal.  But we
>> can't just ban every unknown algorithm because we rely on that
>> to let things like echainiv through.
>> 
>> Let me figure out a way to differentiate these two cases.
>
> So what I've done is modify hmac so that if the underlying algo
> is FIPS_INTERNAL, then we pre-emptively set FIPS_INTERNAL on the
> hmac side as well.  This can then be cleared if the hmac algorithm
> is explicitly listed as fips_allowed.

I wonder whether this can be made more generic. I.e. would it be possible
to make crypto_grab_spawn() to or in FIPS_INTERNAL into type (iff !(mask
& FIPS_INTERNAL)) and to make crypto_register_instance() to propagate
FIPS_INTERNAL from the template instance's spawns upwards into the
instance's ->cra_flags?


> ---8<---
> Currently we do not distinguish between algorithms that fail on
> the self-test vs. those which are disabled in FIPS mode (not allowed).
> Both are marked as having failed the self-test.
>
> As it has been requested that we need to disable sha1 in FIPS
> mode while still allowing hmac(sha1) this approach needs to change.

On an unrelated note, this will break trusted_key_tpm_ops->init() in
FIPS mode, because trusted_shash_alloc() would fail to get a hold of
sha1. AFAICT, this could potentially make the init_trusted() module_init
to fail, and, as encrypted-keys.ko imports key_type_trusted, prevent the
loading of that one as well. Not sure that's desired...


> This patch allows this scenario by adding a new flag FIPS_INTERNAL
> to indicate those algorithms that have passed the self-test and are
> not FIPS-allowed.  They can then be used for the self-testing of
> other algorithms or by those that are explicitly allowed to use them
> (currently just hmac).
>
> Note that as a side-effect of this patch algorithms which are not
> FIPS-allowed will now return ENOENT instead of ELIBBAD.  Hopefully
> this is not an issue as some people were relying on this already.
>
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
>
> diff --git a/crypto/algapi.c b/crypto/algapi.c
> index a366cb3e8aa1..09fb75806e87 100644
> --- a/crypto/algapi.c
> +++ b/crypto/algapi.c
> @@ -322,8 +322,16 @@ void crypto_alg_tested(const char *name, int err)
>  found:
>  	q->cra_flags |= CRYPTO_ALG_DEAD;
>  	alg = test->adult;
> -	if (err || list_empty(&alg->cra_list))
> +
> +	if (list_empty(&alg->cra_list))
> +		goto complete;
> +
> +	if (err == -ECANCELED)
> +		alg->cra_flags |= CRYPTO_ALG_FIPS_INTERNAL;
> +	else if (err)
>  		goto complete;
> +	else
> +		alg->cra_flags &= ~CRYPTO_ALG_FIPS_INTERNAL;
>  
>  	alg->cra_flags |= CRYPTO_ALG_TESTED;
>  
> diff --git a/crypto/api.c b/crypto/api.c
> index cf0869dd130b..549f9aced1da 100644
> --- a/crypto/api.c
> +++ b/crypto/api.c
> @@ -223,6 +223,8 @@ static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg)
>  	else if (crypto_is_test_larval(larval) &&
>  		 !(alg->cra_flags & CRYPTO_ALG_TESTED))
>  		alg = ERR_PTR(-EAGAIN);
> +	else if (alg->cra_flags & CRYPTO_ALG_FIPS_INTERNAL)
> +		alg = ERR_PTR(-EAGAIN);

I might be mistaken, but I think this would cause hmac(sha1)
instantiation to fail if sha1 is not already there. I.e. if hmac(sha1)
instantiation would load sha1, then it would invoke crypto_larval_wait()
on the sha1 larval, see the FIPS_INTERNAL and fail?

However, wouldn't it be possible to simply implement FIPS_INTERNAL
lookups in analogy to the INTERNAL ones instead? That is, would adding

	if (!((type | mask) & CRYPTO_ALG_FIPS_INTERNAL))
		mask |= CRYPTO_ALG_FIPS_INTERNAL;

to the preamble of crypto_alg_mod_lookup() work instead?

AFAICS, ...


>  	else if (!crypto_mod_get(alg))
>  		alg = ERR_PTR(-EAGAIN);
>  	crypto_mod_put(&larval->alg);
> @@ -233,6 +235,7 @@ static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg)
>  static struct crypto_alg *crypto_alg_lookup(const char *name, u32 type,
>  					    u32 mask)
>  {
> +	const u32 fips = CRYPTO_ALG_FIPS_INTERNAL;
>  	struct crypto_alg *alg;
>  	u32 test = 0;
>  
> @@ -240,8 +243,20 @@ static struct crypto_alg *crypto_alg_lookup(const char *name, u32 type,
>  		test |= CRYPTO_ALG_TESTED;
>  
>  	down_read(&crypto_alg_sem);
> -	alg = __crypto_alg_lookup(name, type | test, mask | test);
> -	if (!alg && test) {
> +	alg = __crypto_alg_lookup(name, (type | test) & ~fips,
> +				  (mask | test) & ~fips);
> +	if (alg) {
> +		if (((type | mask) ^ fips) & fips)
> +			mask |= fips;
> +		mask &= fips;
> +
> +		if (!crypto_is_larval(alg) &&
> +		    ((type ^ alg->cra_flags) & mask)) {
> +			/* Algorithm is disallowed in FIPS mode. */
> +			crypto_mod_put(alg);
> +			alg = ERR_PTR(-ENOENT);
> +		}
> +	} else if (test) {

... this check here would not be needed anymore then? But I might be
missing something.


>  		alg = __crypto_alg_lookup(name, type, mask);
>  		if (alg && !crypto_is_larval(alg)) {
>  			/* Test failed */
> diff --git a/crypto/hmac.c b/crypto/hmac.c
> index 25856aa7ccbf..af82e3eeb7d0 100644
> --- a/crypto/hmac.c
> +++ b/crypto/hmac.c
> @@ -169,6 +169,7 @@ static int hmac_create(struct crypto_template *tmpl, struct rtattr **tb)
>  	struct crypto_alg *alg;
>  	struct shash_alg *salg;
>  	u32 mask;
> +	u32 type;
>  	int err;
>  	int ds;
>  	int ss;
> @@ -182,8 +183,9 @@ static int hmac_create(struct crypto_template *tmpl, struct rtattr **tb)
>  		return -ENOMEM;
>  	spawn = shash_instance_ctx(inst);
>  
> +	type = CRYPTO_ALG_FIPS_INTERNAL;
>  	err = crypto_grab_shash(spawn, shash_crypto_instance(inst),
> -				crypto_attr_alg_name(tb[1]), 0, mask);
> +				crypto_attr_alg_name(tb[1]), type, mask);
>  	if (err)
>  		goto err_free_inst;
>  	salg = crypto_spawn_shash_alg(spawn);
> @@ -204,6 +206,7 @@ static int hmac_create(struct crypto_template *tmpl, struct rtattr **tb)
>  	if (err)
>  		goto err_free_inst;
>  
> +	inst->alg.base.cra_flags = alg->cra_flags & CRYPTO_ALG_FIPS_INTERNAL;
>  	inst->alg.base.cra_priority = alg->cra_priority;
>  	inst->alg.base.cra_blocksize = alg->cra_blocksize;
>  	inst->alg.base.cra_alignmask = alg->cra_alignmask;
> diff --git a/crypto/testmgr.c b/crypto/testmgr.c
> index 5831d4bbc64f..995d44db6154 100644
> --- a/crypto/testmgr.c
> +++ b/crypto/testmgr.c
> @@ -1664,7 +1664,8 @@ static int test_hash_vs_generic_impl(const char *generic_driver,
>  	if (strcmp(generic_driver, driver) == 0) /* Already the generic impl? */
>  		return 0;
>  
> -	generic_tfm = crypto_alloc_shash(generic_driver, 0, 0);
> +	generic_tfm = crypto_alloc_shash(generic_driver,
> +					 CRYPTO_ALG_FIPS_INTERNAL, 0);
>  	if (IS_ERR(generic_tfm)) {
>  		err = PTR_ERR(generic_tfm);
>  		if (err == -ENOENT) {
> @@ -2387,7 +2388,8 @@ static int test_aead_vs_generic_impl(struct aead_extra_tests_ctx *ctx)
>  	if (strcmp(generic_driver, driver) == 0) /* Already the generic impl? */
>  		return 0;
>  
> -	generic_tfm = crypto_alloc_aead(generic_driver, 0, 0);
> +	generic_tfm = crypto_alloc_aead(generic_driver,
> +					CRYPTO_ALG_FIPS_INTERNAL, 0);
>  	if (IS_ERR(generic_tfm)) {
>  		err = PTR_ERR(generic_tfm);
>  		if (err == -ENOENT) {
> @@ -2986,7 +2988,8 @@ static int test_skcipher_vs_generic_impl(const char *generic_driver,
>  	if (strcmp(generic_driver, driver) == 0) /* Already the generic impl? */
>  		return 0;
>  
> -	generic_tfm = crypto_alloc_skcipher(generic_driver, 0, 0);
> +	generic_tfm = crypto_alloc_skcipher(generic_driver,
> +					    CRYPTO_ALG_FIPS_INTERNAL, 0);
>  	if (IS_ERR(generic_tfm)) {
>  		err = PTR_ERR(generic_tfm);
>  		if (err == -ENOENT) {
> @@ -5328,7 +5331,6 @@ static const struct alg_test_desc alg_test_descs[] = {
>  	}, {
>  		.alg = "sha1",
>  		.test = alg_test_hash,
> -		.fips_allowed = 1,
>  		.suite = {
>  			.hash = __VECS(sha1_tv_template)
>  		}
> @@ -5613,6 +5615,13 @@ static int alg_find_test(const char *alg)
>  	return -1;
>  }
>  
> +static int alg_fips_disabled(const char *driver, const char *alg)
> +{
> +	pr_info("alg: %s (%s) is disabled due to FIPS\n", alg, driver);
> +
> +	return -ECANCELED;
> +}
> +
>  int alg_test(const char *driver, const char *alg, u32 type, u32 mask)
>  {
>  	int i;
> @@ -5637,9 +5646,6 @@ int alg_test(const char *driver, const char *alg, u32 type, u32 mask)
>  		if (i < 0)
>  			goto notest;
>  
> -		if (fips_enabled && !alg_test_descs[i].fips_allowed)
> -			goto non_fips_alg;
> -
>  		rc = alg_test_cipher(alg_test_descs + i, driver, type, mask);
>  		goto test_done;
>  	}
> @@ -5649,8 +5655,7 @@ int alg_test(const char *driver, const char *alg, u32 type, u32 mask)
>  	if (i < 0 && j < 0)
>  		goto notest;
>  
> -	if (fips_enabled && ((i >= 0 && !alg_test_descs[i].fips_allowed) ||
> -			     (j >= 0 && !alg_test_descs[j].fips_allowed)))
> +	if (fips_enabled && (j >= 0 && !alg_test_descs[j].fips_allowed))
>  		goto non_fips_alg;
>  
>  	rc = 0;
> @@ -5671,16 +5676,22 @@ int alg_test(const char *driver, const char *alg, u32 type, u32 mask)
>  		}
>  		WARN(1, "alg: self-tests for %s (%s) failed (rc=%d)",
>  		     driver, alg, rc);
> -	} else {
> -		if (fips_enabled)
> -			pr_info("alg: self-tests for %s (%s) passed\n",
> -				driver, alg);
> +	} else if (fips_enabled) {
> +		pr_info("alg: self-tests for %s (%s) passed\n",
> +			driver, alg);
> +
> +		if (i >= 0 && !alg_test_descs[i].fips_allowed)
> +			rc = alg_fips_disabled(driver, alg);
>  	}
>  
>  	return rc;
>  
>  notest:
>  	printk(KERN_INFO "alg: No test for %s (%s)\n", alg, driver);
> +
> +	if (type & CRYPTO_ALG_FIPS_INTERNAL)
> +		return alg_fips_disabled(driver, alg);
> +
>  	return 0;
>  non_fips_alg:
>  	return -EINVAL;

This looks all good to me, but as !->fips_allowed tests aren't skipped
over anymore now, it would perhaps make sense to make their failure
non-fatal in FIPS mode. Because in FIPS mode a failure could mean a
panic and some of the existing TVs might not pass because of e.g. some
key length checks or so active only for fips_enabled...

Many thanks again!

Nicolai



> diff --git a/include/linux/crypto.h b/include/linux/crypto.h
> index 855869e1fd32..df3f68dfe8c7 100644
> --- a/include/linux/crypto.h
> +++ b/include/linux/crypto.h
> @@ -132,6 +132,15 @@
>   */
>  #define CRYPTO_ALG_ALLOCATES_MEMORY	0x00010000
>  
> +/*
> + * Mark an algorithm as a service implementation only usable by a
> + * template and never by a normal user of the kernel crypto API.
> + * This is intended to be used by algorithms that are themselves
> + * not FIPS-approved but may instead be used to implement parts of
> + * a FIPS-approved algorithm (e.g., sha1 vs. hmac(sha1)).
> + */
> +#define CRYPTO_ALG_FIPS_INTERNAL	0x00020000
> +
>  /*
>   * Transform masks and values (for crt_flags).
>   */

-- 
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg), GF: Ivo Totev

  reply	other threads:[~2022-01-14  9:09 UTC|newest]

Thread overview: 53+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-09  9:03 [PATCH v2 00/18] crypto: dh - infrastructure for NVM in-band auth and FIPS conformance Nicolai Stange
2021-12-09  9:03 ` [PATCH v2 01/18] crypto: dh - remove struct dh's ->q member Nicolai Stange
2021-12-09  9:03 ` [PATCH v2 02/18] crypto: dh - constify struct dh's pointer members Nicolai Stange
2021-12-09  9:03 ` [PATCH v2 03/18] crypto: dh - optimize domain parameter serialization for well-known groups Nicolai Stange
2021-12-10 11:33   ` Hannes Reinecke
2021-12-13 10:06     ` Nicolai Stange
2021-12-13 10:10       ` Hannes Reinecke
2021-12-17  5:52   ` Herbert Xu
2021-12-20 15:27     ` Nicolai Stange
2021-12-29  2:14       ` Herbert Xu
2022-01-06 14:30         ` Stephan Mueller
2022-01-07  2:44           ` Herbert Xu
2022-01-07  6:37             ` Nicolai Stange
2022-01-11  6:13             ` [PATCH] crypto: api - Disallow sha1 in FIPS-mode while allowing hmac(sha1) Herbert Xu
2022-01-11  7:50               ` Nicolai Stange
2022-01-11 10:34                 ` Herbert Xu
2022-01-14  6:16                   ` [v2 PATCH] " Herbert Xu
2022-01-14  9:09                     ` Nicolai Stange [this message]
2022-01-14 10:55                       ` Herbert Xu
2022-01-14 12:34                         ` Nicolai Stange
2022-01-14 12:35                         ` Stephan Mueller
2022-01-14 12:54                           ` James Bottomley
2022-01-26  9:01                         ` Stephan Mueller
2022-01-28 14:14                         ` Nicolai Stange
2022-01-28 15:49                           ` Stephan Mueller
2022-02-02 10:09                             ` Nicolai Stange
2022-01-07  7:01         ` [PATCH v2 03/18] crypto: dh - optimize domain parameter serialization for well-known groups Nicolai Stange
2021-12-09  9:03 ` [PATCH v2 04/18] crypto: dh - introduce RFC 7919 safe-prime groups Nicolai Stange
2021-12-10 11:34   ` Hannes Reinecke
2021-12-09  9:03 ` [PATCH v2 05/18] crypto: testmgr - add DH RFC 7919 ffdhe3072 test vector Nicolai Stange
2021-12-10 11:34   ` Hannes Reinecke
2021-12-09  9:03 ` [PATCH v2 06/18] crypto: dh - introduce RFC 3526 safe-prime groups Nicolai Stange
2021-12-10 11:35   ` Hannes Reinecke
2021-12-09  9:03 ` [PATCH v2 07/18] crypto: testmgr - add DH RFC 3526 modp2048 test vector Nicolai Stange
2021-12-09  9:03 ` [PATCH v2 08/18] crypto: testmgr - run only subset of DH vectors based on config Nicolai Stange
2021-12-10 11:36   ` Hannes Reinecke
2021-12-09  9:03 ` [PATCH v2 09/18] crypto: dh - implement private key generation primitive Nicolai Stange
2021-12-09  9:03 ` [PATCH v2 10/18] crypto: dh - introduce support for ephemeral key generation to dh-generic Nicolai Stange
2021-12-09  9:03 ` [PATCH v2 11/18] crypto: dh - introduce support for ephemeral key generation to hpre driver Nicolai Stange
2021-12-09  9:03 ` [PATCH v2 12/18] crypto: dh - introduce support for ephemeral key generation to qat driver Nicolai Stange
2021-12-15 21:54   ` Giovanni Cabiddu
2021-12-09  9:03 ` [PATCH v2 13/18] crypto: testmgr - add DH test vectors for key generation Nicolai Stange
2021-12-10 11:37   ` Hannes Reinecke
2021-12-09  9:03 ` [PATCH v2 14/18] lib/mpi: export mpi_rshift Nicolai Stange
2021-12-09  9:03 ` [PATCH v2 15/18] crypto: dh - store group id in dh-generic's dh_ctx Nicolai Stange
2021-12-09  9:03 ` [PATCH v2 16/18] crypto: dh - calculate Q from P for the full public key verification Nicolai Stange
2021-12-09  9:03 ` [PATCH v2 17/18] crypto: dh - try to match domain parameters to a known safe-prime group Nicolai Stange
2021-12-09  9:03 ` [PATCH v2 18/18] crypto: dh - accept only approved safe-prime groups in FIPS mode Nicolai Stange
2021-12-10 11:37   ` Hannes Reinecke
2021-12-10  7:56 ` [PATCH v2 00/18] crypto: dh - infrastructure for NVM in-band auth and FIPS conformance Stephan Mueller
2021-12-10 10:00   ` Nicolai Stange
2021-12-10 11:38 ` Hannes Reinecke
2021-12-13 10:12   ` Nicolai Stange

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87k0f2hefl.fsf@suse.de \
    --to=nstange@suse.de \
    --cc=davem@davemloft.net \
    --cc=dhowells@redhat.com \
    --cc=duwe@suse.de \
    --cc=ebiggers@kernel.org \
    --cc=giovanni.cabiddu@intel.com \
    --cc=hare@suse.de \
    --cc=herbert@gondor.apana.org.au \
    --cc=jarkko@kernel.org \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pvorel@suse.cz \
    --cc=qat-linux@intel.com \
    --cc=simo@redhat.com \
    --cc=smueller@chronox.de \
    --cc=xuzaibo@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.