Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

crypto: api - allow algs only in specific constructions in FIPS mode

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.

Recently the need arose to allow the usage of certain algorithms only
as arguments to specific template instantiations in FIPS mode. For
example, standalone "dh" must be blocked, but e.g. "ffdhe2048(dh)" is
allowed. Other potential use cases include "cbcmac(aes)", which must
only be used with ccm(), or "ghash", which must be used only for
gcm().

This patch allows this scenario by adding a new flag FIPS_INTERNAL to
indicate those algorithms that are not FIPS-allowed. They can then be
used as template arguments only, i.e. when looked up via
crypto_grab_spawn() to be more specific. The FIPS_INTERNAL bit gets
propagated upwards recursively into the surrounding template
instances, until the construction eventually matches an explicit
testmgr entry with ->fips_allowed being set, if any.

The behaviour to skip !->fips_allowed self-test executions in FIPS
mode will be retained. Note that this effectively means that
FIPS_INTERNAL algorithms are handled very similarly to the INTERNAL
ones in this regard. It is expected that the FIPS_INTERNAL algorithms
will receive sufficient testing when the larger constructions they're
a part of, if any, get exercised by testmgr.

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.

Link: https://lore.kernel.org/r/YeEVSaMEVJb3cQkq@gondor.apana.org.au
Originally-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Nicolai Stange <nstange@suse.de>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

Nicolai Stange and committed by
Herbert Xu
d6097b8d c8e8236c

+63 -10
+16 -2
crypto/algapi.c
··· 328 328 found: 329 329 q->cra_flags |= CRYPTO_ALG_DEAD; 330 330 alg = test->adult; 331 - if (err || list_empty(&alg->cra_list)) 331 + 332 + if (list_empty(&alg->cra_list)) 332 333 goto complete; 334 + 335 + if (err == -ECANCELED) 336 + alg->cra_flags |= CRYPTO_ALG_FIPS_INTERNAL; 337 + else if (err) 338 + goto complete; 339 + else 340 + alg->cra_flags &= ~CRYPTO_ALG_FIPS_INTERNAL; 333 341 334 342 alg->cra_flags |= CRYPTO_ALG_TESTED; 335 343 ··· 618 610 { 619 611 struct crypto_larval *larval; 620 612 struct crypto_spawn *spawn; 613 + u32 fips_internal = 0; 621 614 int err; 622 615 623 616 err = crypto_check_alg(&inst->alg); ··· 641 632 spawn->inst = inst; 642 633 spawn->registered = true; 643 634 635 + fips_internal |= spawn->alg->cra_flags; 636 + 644 637 crypto_mod_put(spawn->alg); 645 638 646 639 spawn = next; 647 640 } 641 + 642 + inst->alg.cra_flags |= (fips_internal & CRYPTO_ALG_FIPS_INTERNAL); 648 643 649 644 larval = __crypto_register_alg(&inst->alg); 650 645 if (IS_ERR(larval)) ··· 702 689 if (IS_ERR(name)) 703 690 return PTR_ERR(name); 704 691 705 - alg = crypto_find_alg(name, spawn->frontend, type, mask); 692 + alg = crypto_find_alg(name, spawn->frontend, 693 + type | CRYPTO_ALG_FIPS_INTERNAL, mask); 706 694 if (IS_ERR(alg)) 707 695 return PTR_ERR(alg); 708 696
+17 -2
crypto/api.c
··· 223 223 else if (crypto_is_test_larval(larval) && 224 224 !(alg->cra_flags & CRYPTO_ALG_TESTED)) 225 225 alg = ERR_PTR(-EAGAIN); 226 + else if (alg->cra_flags & CRYPTO_ALG_FIPS_INTERNAL) 227 + alg = ERR_PTR(-EAGAIN); 226 228 else if (!crypto_mod_get(alg)) 227 229 alg = ERR_PTR(-EAGAIN); 228 230 crypto_mod_put(&larval->alg); ··· 235 233 static struct crypto_alg *crypto_alg_lookup(const char *name, u32 type, 236 234 u32 mask) 237 235 { 236 + const u32 fips = CRYPTO_ALG_FIPS_INTERNAL; 238 237 struct crypto_alg *alg; 239 238 u32 test = 0; 240 239 ··· 243 240 test |= CRYPTO_ALG_TESTED; 244 241 245 242 down_read(&crypto_alg_sem); 246 - alg = __crypto_alg_lookup(name, type | test, mask | test); 247 - if (!alg && test) { 243 + alg = __crypto_alg_lookup(name, (type | test) & ~fips, 244 + (mask | test) & ~fips); 245 + if (alg) { 246 + if (((type | mask) ^ fips) & fips) 247 + mask |= fips; 248 + mask &= fips; 249 + 250 + if (!crypto_is_larval(alg) && 251 + ((type ^ alg->cra_flags) & mask)) { 252 + /* Algorithm is disallowed in FIPS mode. */ 253 + crypto_mod_put(alg); 254 + alg = ERR_PTR(-ENOENT); 255 + } 256 + } else if (test) { 248 257 alg = __crypto_alg_lookup(name, type, mask); 249 258 if (alg && !crypto_is_larval(alg)) { 250 259 /* Test failed */
+2 -2
crypto/tcrypt.c
··· 1473 1473 pr_debug("testing %s\n", alg); 1474 1474 1475 1475 ret = alg_test(alg, alg, 0, 0); 1476 - /* non-fips algs return -EINVAL in fips mode */ 1477 - if (fips_enabled && ret == -EINVAL) 1476 + /* non-fips algs return -EINVAL or -ECANCELED in fips mode */ 1477 + if (fips_enabled && (ret == -EINVAL || ret == -ECANCELED)) 1478 1478 ret = 0; 1479 1479 return ret; 1480 1480 }
+19 -4
crypto/testmgr.c
··· 5650 5650 return -1; 5651 5651 } 5652 5652 5653 + static int alg_fips_disabled(const char *driver, const char *alg) 5654 + { 5655 + pr_info("alg: %s (%s) is disabled due to FIPS\n", alg, driver); 5656 + 5657 + return -ECANCELED; 5658 + } 5659 + 5653 5660 int alg_test(const char *driver, const char *alg, u32 type, u32 mask) 5654 5661 { 5655 5662 int i; ··· 5693 5686 if (i < 0 && j < 0) 5694 5687 goto notest; 5695 5688 5696 - if (fips_enabled && ((i >= 0 && !alg_test_descs[i].fips_allowed) || 5697 - (j >= 0 && !alg_test_descs[j].fips_allowed))) 5698 - goto non_fips_alg; 5689 + if (fips_enabled) { 5690 + if (j >= 0 && !alg_test_descs[j].fips_allowed) 5691 + return -EINVAL; 5692 + 5693 + if (i >= 0 && !alg_test_descs[i].fips_allowed) 5694 + goto non_fips_alg; 5695 + } 5699 5696 5700 5697 rc = 0; 5701 5698 if (i >= 0) ··· 5729 5718 5730 5719 notest: 5731 5720 printk(KERN_INFO "alg: No test for %s (%s)\n", alg, driver); 5721 + 5722 + if (type & CRYPTO_ALG_FIPS_INTERNAL) 5723 + return alg_fips_disabled(driver, alg); 5724 + 5732 5725 return 0; 5733 5726 non_fips_alg: 5734 - return -EINVAL; 5727 + return alg_fips_disabled(driver, alg); 5735 5728 } 5736 5729 5737 5730 #endif /* CONFIG_CRYPTO_MANAGER_DISABLE_TESTS */
+9
include/linux/crypto.h
··· 133 133 #define CRYPTO_ALG_ALLOCATES_MEMORY 0x00010000 134 134 135 135 /* 136 + * Mark an algorithm as a service implementation only usable by a 137 + * template and never by a normal user of the kernel crypto API. 138 + * This is intended to be used by algorithms that are themselves 139 + * not FIPS-approved but may instead be used to implement parts of 140 + * a FIPS-approved algorithm (e.g., dh vs. ffdhe2048(dh)). 141 + */ 142 + #define CRYPTO_ALG_FIPS_INTERNAL 0x00020000 143 + 144 + /* 136 145 * Transform masks and values (for crt_flags). 137 146 */ 138 147 #define CRYPTO_TFM_NEED_KEY 0x00000001