diff --git a/BUILD.bazel b/BUILD.bazel index 2403cbc8869f..5fc9850b2904 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1297,6 +1297,31 @@ pkg_install( destdir = "out/yukawa/dist", ) +kernel_build( + name = "fips140", + outs = [], + base_kernel = ":kernel_aarch64", + build_config = "build.config.common", + defconfig = "arch/arm64/configs/gki_defconfig", + make_goals = ["modules"], + makefile = ":Makefile", + module_outs = ["crypto/fips140.ko"], + post_defconfig_fragments = ["arch/arm64/configs/fips140_gki.fragment"], +) + +pkg_files( + name = "fips140_dist_files", + srcs = [":fips140"], + strip_prefix = strip_prefix.files_only(), + visibility = ["//visibility:private"], +) + +pkg_install( + name = "fips140_dist", + srcs = ["fips140_dist_files"], + destdir = "out/fips140/dist", +) + # allmodconfig build tests. # These are build tests only, so: # - outs are intentionally set to empty to not copy anything to DIST_DIR diff --git a/arch/arm64/Makefile.postlink b/arch/arm64/Makefile.postlink new file mode 100644 index 000000000000..b2717bab9230 --- /dev/null +++ b/arch/arm64/Makefile.postlink @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0 + +# +# This file is included by the generic Kbuild makefile to permit the +# architecture to perform postlink actions on vmlinux and any .ko module file. +# In this case, we only need it for fips140.ko, which needs some postprocessing +# for the integrity check mandated by FIPS. This involves making copies of the +# relocation sections so that the module will have access to them at +# initialization time, and calculating and injecting a HMAC digest into the +# module. All other targets are NOPs. +# + +PHONY := __archpost +__archpost: + +-include include/config/auto.conf +include $(srctree)/scripts/Kbuild.include + +CMD_FIPS140_GEN_HMAC = crypto/fips140_gen_hmac +quiet_cmd_gen_hmac = HMAC $@ + cmd_gen_hmac = $(OBJCOPY) $@ \ + --dump-section=$(shell $(READELF) -SW $@|grep -Eo '\.rela\.text\S*')=$@.rela.text \ + --dump-section=$(shell $(READELF) -SW $@|grep -Eo '\.rela\.rodata\S*')=$@.rela.rodata && \ + $(OBJCOPY) $@ \ + --add-section=.init.rela.text=$@.rela.text \ + --add-section=.init.rela.rodata=$@.rela.rodata \ + --set-section-flags=.init.rela.text=alloc,readonly \ + --set-section-flags=.init.rela.rodata=alloc,readonly && \ + $(CMD_FIPS140_GEN_HMAC) $@ + +# `@true` prevents complaints when there is nothing to be done + +vmlinux: FORCE + @true + +$(objtree)/crypto/fips140.ko: FORCE + $(call cmd,gen_hmac) + +%.ko: FORCE + @true + +clean: + rm -f $(objtree)/crypto/fips140.ko.rela.* + +PHONY += FORCE clean + +FORCE: + +.PHONY: $(PHONY) diff --git a/arch/arm64/configs/fips140_gki.fragment b/arch/arm64/configs/fips140_gki.fragment new file mode 100644 index 000000000000..aa8444d34e56 --- /dev/null +++ b/arch/arm64/configs/fips140_gki.fragment @@ -0,0 +1,2 @@ +CONFIG_CRYPTO_FIPS140_MOD=m +# CONFIG_MODULE_SIG_ALL is not set diff --git a/arch/arm64/crypto/Kbuild.fips140 b/arch/arm64/crypto/Kbuild.fips140 new file mode 100644 index 000000000000..9aa0af602130 --- /dev/null +++ b/arch/arm64/crypto/Kbuild.fips140 @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Create a separate FIPS archive that duplicates the modules that are relevant +# for FIPS 140 certification as builtin objects +# + +sha1-ce-y := sha1-ce-glue.o sha1-ce-core.o +sha2-ce-y := sha2-ce-glue.o sha2-ce-core.o +sha512-ce-y := sha512-ce-glue.o sha512-ce-core.o +ghash-ce-y := ghash-ce-glue.o ghash-ce-core.o +aes-ce-cipher-y := aes-ce-core.o aes-ce-glue.o +aes-ce-blk-y := aes-glue-ce.o aes-ce.o +aes-neon-blk-y := aes-glue-neon.o aes-neon.o +sha256-arm64-y := sha256-glue.o sha256-core.o +sha512-arm64-y := sha512-glue.o sha512-core.o +aes-arm64-y := aes-cipher-core.o aes-cipher-glue.o +aes-neon-bs-y := aes-neonbs-core.o aes-neonbs-glue.o + +crypto-arm64-fips-src := $(srctree)/arch/arm64/crypto/ +crypto-arm64-fips-modules := sha1-ce.o sha2-ce.o sha512-ce.o ghash-ce.o \ + aes-ce-cipher.o aes-ce-blk.o aes-neon-blk.o \ + sha256-arm64.o sha512-arm64.o aes-arm64.o \ + aes-neon-bs.o + +crypto-fips-objs += $(foreach o,$(crypto-arm64-fips-modules),$($(o:.o=-y):.o=-fips-arch.o)) + +CFLAGS_aes-glue-ce-fips-arch.o := -DUSE_V8_CRYPTO_EXTENSIONS + +$(obj)/aes-glue-%-fips-arch.o: KBUILD_CFLAGS += $(FIPS140_CFLAGS) +$(obj)/aes-glue-%-fips-arch.o: $(crypto-arm64-fips-src)/aes-glue.c FORCE + $(call if_changed_rule,cc_o_c) + +$(obj)/%-fips-arch.o: KBUILD_CFLAGS += $(FIPS140_CFLAGS) +$(obj)/%-fips-arch.o: $(crypto-arm64-fips-src)/%.c FORCE + $(call if_changed_rule,cc_o_c) + +$(obj)/%-fips-arch.o: $(crypto-arm64-fips-src)/%.S FORCE + $(call if_changed_rule,as_o_S) + +quiet_cmd_perlasm = PERLASM $@ + cmd_perlasm = $(PERL) $(<) void $(@) + +$(obj)/%-core.S: $(crypto-arm64-fips-src)/%-armv8.pl + $(call cmd,perlasm) + +$(obj)/sha256-core.S: $(crypto-arm64-fips-src)/sha512-armv8.pl + $(call cmd,perlasm) + +clean-files += sha256-core.S sha512-core.S + +$(obj)/%-fips-arch.o: $(obj)/%.S FORCE + $(call if_changed_rule,as_o_S) diff --git a/crypto/Kconfig b/crypto/Kconfig index a779cab668c2..65f9fe7fa195 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -54,6 +54,69 @@ config CRYPTO_FIPS_VERSION This option provides the ability to override the FIPS Module Version. By default the KERNELRELEASE value is used. +config CRYPTO_FIPS140_MOD + tristate "Enable FIPS 140 cryptographic module" + depends on ARM64 + depends on CC_IS_CLANG + depends on m + select CRYPTO_FIPS140_MERGE_MOD_SECTIONS + help + This option enables building a loadable module fips140.ko, which + contains various crypto algorithms that are also built into vmlinux. + At load time, this module overrides the built-in implementations of + these algorithms with its implementations. It also runs self-tests on + these algorithms and verifies the integrity of its code and data. If + either of these steps fails, the kernel will panic. + + This module is intended to be loaded at early boot time in order to + meet FIPS 140 and NIAP FPT_TST_EXT.1 requirements. It shouldn't be + used if you don't need to meet these requirements. + +config CRYPTO_FIPS140_MOD_EVAL_TESTING + bool "Enable evaluation testing features in FIPS 140 module" + depends on CRYPTO_FIPS140_MOD + help + This option adds some features to the FIPS 140 module which are needed + for lab evaluation testing of the module, e.g. support for injecting + errors and support for a userspace interface to some of the module's + services. This option should not be enabled in production builds. + +config CRYPTO_FIPS140_MERGE_MOD_SECTIONS + bool + help + This option causes the module linker script to place the delimeters + of the text and rodata sections at the appropriate places so that + the FIPS 140 integrity check can be performed. This option is required + by the Crypto FIPS 140 module, and can be enabled by other FIPS 140 + modules using the same logic to perform the self integrity check. + +config CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK + bool "Debug the integrity check in FIPS 140 module" + depends on CRYPTO_FIPS140_MOD + help + This option makes the FIPS 140 module provide debugfs files containing + the text and rodata that were used for the integrity check, i.e. the + runtime text and rodata with relocations and code patches unapplied. + This option also makes the module load even if the integrity check + fails so that these files can be used to debug the failure. (A + possible failure mode is that the kernel has added a new type of code + patching and the module needs to be updated to disable or unapply it.) + + This option must not be enabled in production builds. + + Example commands for debugging an integrity check failure: + + adb root + adb shell mount debugfs -t debugfs /sys/kernel/debug + adb shell cp /sys/kernel/debug/fips140/{text,rodata} /data/local/tmp/ + adb pull /data/local/tmp/text text.checked + adb pull /data/local/tmp/rodata rodata.checked + llvm-objcopy -O binary --only-section=.text fips140.ko text.orig + llvm-objcopy -O binary --only-section=.rodata fips140.ko rodata.orig + for f in {text,rodata}.{orig,checked}; do xxd -g1 $f > $f.xxd; done + vimdiff text.{orig,checked}.xxd + vimdiff rodata.{orig,checked}.xxd + config CRYPTO_ALGAPI tristate select CRYPTO_ALGAPI2 diff --git a/crypto/Makefile b/crypto/Makefile index 4c99e5d376f6..3ecfaf515823 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -208,3 +208,57 @@ obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o # Key derivation function # obj-$(CONFIG_CRYPTO_KDF800108_CTR) += kdf_sp800108.o + +ifneq ($(CONFIG_CRYPTO_FIPS140_MOD),) + +FIPS140_CFLAGS := -DBUILD_FIPS140_KO -include $(srctree)/crypto/fips140-defs.h + +CFLAGS_jitterentropy-fips.o := -O0 +KASAN_SANITIZE_jitterentropy-fips.o = n +UBSAN_SANITIZE_jitterentropy-fips.o = n + +# Compile an extra copy of various crypto algorithms into the fips140 module. +# +# Note: the module will still work if some files are removed from here. +# However, it may affect FIPS certifiability. Don't remove files from here +# without considering impact on FIPS certifiability. + +crypto-fips-objs := drbg.o ecb.o cbc.o ctr.o cts.o gcm.o xts.o hmac.o cmac.o \ + aes_generic.o \ + jitterentropy.o jitterentropy-kcapi.o \ + sha1_generic.o sha256_generic.o sha512_generic.o \ + sha3_generic.o \ + lib-crypto-aes.o lib-crypto-gf128mul.o lib-crypto-memneq.o \ + lib-crypto-sha1.o lib-crypto-sha256.o lib-crypto-utils.o +crypto-fips-objs := $(foreach o,$(crypto-fips-objs),$(o:.o=-fips.o)) + +# get the arch to add its objects to $(crypto-fips-objs) +include $(srctree)/arch/$(ARCH)/crypto/Kbuild.fips140 + +$(obj)/%-fips.o: KBUILD_CFLAGS += $(FIPS140_CFLAGS) +$(obj)/%-fips.o: $(src)/%.c FORCE + $(call if_changed_rule,cc_o_c) +$(obj)/lib-%-fips.o: $(srctree)/lib/%.c FORCE + $(call if_changed_rule,cc_o_c) +$(obj)/lib-crypto-%-fips.o: $(srctree)/lib/crypto/%.c FORCE + $(call if_changed_rule,cc_o_c) + +fips140-objs := \ + fips140-alg-registration.o \ + fips140-module.o \ + fips140-refs.o \ + fips140-selftests.o \ + $(crypto-fips-objs) +fips140-$(CONFIG_CRYPTO_FIPS140_MOD_EVAL_TESTING) += \ + fips140-eval-testing.o +obj-m += fips140.o + +CFLAGS_fips140-alg-registration.o += $(FIPS140_CFLAGS) +CFLAGS_fips140-module.o += $(FIPS140_CFLAGS) +CFLAGS_fips140-selftests.o += $(FIPS140_CFLAGS) +CFLAGS_fips140-eval-testing.o += $(FIPS140_CFLAGS) + +hostprogs-always-y := fips140_gen_hmac +HOSTLDLIBS_fips140_gen_hmac := -lcrypto -lelf + +endif diff --git a/crypto/fips140-alg-registration.c b/crypto/fips140-alg-registration.c new file mode 100644 index 000000000000..03757f88890b --- /dev/null +++ b/crypto/fips140-alg-registration.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Block crypto operations until tests complete + * + * Copyright 2021 Google LLC + * + * This file defines the fips140_crypto_register_*() functions, to which all + * calls to crypto_register_*() in the module are redirected. These functions + * override the tfm initialization function of each algorithm to insert a wait + * for the module having completed its self-tests and integrity check. + * + * The exact field that we override depends on the algorithm type. For + * algorithm types that have a strongly-typed initialization function pointer + * (e.g. skcipher), we must override that, since cra_init isn't guaranteed to be + * called for those despite the field being present in the base struct. For the + * other algorithm types (e.g. "cipher") we must override cra_init. + * + * All of this applies to both normal algorithms and template instances. + * + * The purpose of all of this is to meet a FIPS requirement where the module + * must not produce any output from cryptographic algorithms until it completes + * its tests. Technically this is impossible, but this solution meets the + * intent of the requirement, assuming the user makes a supported sequence of + * API calls. Note that we can't simply run the tests before registering the + * algorithms, as the algorithms must be registered in order to run the tests. + * + * It would be much easier to handle this in the kernel's crypto API framework. + * Unfortunately, that was deemed insufficient because the module itself is + * required to do the enforcement. What is *actually* required is still very + * vague, but the approach implemented here should meet the requirement. + */ + +/* + * This file is the one place in fips140.ko that needs to call the kernel's real + * algorithm registration functions, so #undefine all the macros from + * fips140-defs.h so that the "fips140_" prefix doesn't automatically get added. + */ +#undef aead_register_instance +#undef ahash_register_instance +#undef crypto_register_aead +#undef crypto_register_aeads +#undef crypto_register_ahash +#undef crypto_register_ahashes +#undef crypto_register_alg +#undef crypto_register_algs +#undef crypto_register_rng +#undef crypto_register_rngs +#undef crypto_register_shash +#undef crypto_register_shashes +#undef crypto_register_skcipher +#undef crypto_register_skciphers +#undef shash_register_instance +#undef skcipher_register_instance + +#include +#include +#include +#include +#include +#include + +#include "fips140-module.h" + +/* Indicates whether the self-tests and integrity check have completed */ +DECLARE_COMPLETION(fips140_tests_done); + +/* The thread running the self-tests and integrity check */ +struct task_struct *fips140_init_thread; + +/* + * Map from crypto_alg to original initialization function (possibly NULL) + * + * Note: unregistering an algorithm will leak its map entry, as we don't bother + * to remove it. This should be fine since fips140.ko can't be unloaded. The + * proper solution would be to store the original function pointer in a new + * field in 'struct crypto_alg', but that would require kernel support. + */ +static DEFINE_XARRAY(fips140_init_func_map); + +static bool fips140_ready(void) +{ + return completion_done(&fips140_tests_done); +} + +/* + * Wait until crypto operations are allowed to proceed. Return true if the + * tests are done, or false if the caller is the thread running the tests so it + * is allowed to proceed anyway. + */ +static bool fips140_wait_until_ready(struct crypto_alg *alg) +{ + if (fips140_ready()) + return true; + /* + * The thread running the tests must not wait. Since tfms can only be + * allocated in task context, we can reliably determine whether the + * invocation is from that thread or not by checking 'current'. + */ + if (current == fips140_init_thread) + return false; + + pr_info("blocking user of %s until tests complete\n", + alg->cra_driver_name); + wait_for_completion(&fips140_tests_done); + pr_info("tests done, allowing %s to proceed\n", alg->cra_driver_name); + return true; +} + +static int fips140_store_init_function(struct crypto_alg *alg, void *func) +{ + void *ret; + + /* + * The XArray API requires 4-byte aligned values. Although function + * pointers in general aren't guaranteed to be 4-byte aligned, it should + * be the case for the platforms this module is used on. + */ + if (WARN_ON((unsigned long)func & 3)) + return -EINVAL; + + ret = xa_store(&fips140_init_func_map, (unsigned long)alg, func, + GFP_KERNEL); + return xa_err(ret); +} + +/* Get the algorithm's original initialization function (possibly NULL) */ +static void *fips140_load_init_function(struct crypto_alg *alg) +{ + return xa_load(&fips140_init_func_map, (unsigned long)alg); +} + +/* tfm initialization function overrides */ + +static int fips140_alg_init_tfm(struct crypto_tfm *tfm) +{ + struct crypto_alg *alg = tfm->__crt_alg; + int (*cra_init)(struct crypto_tfm *tfm) = + fips140_load_init_function(alg); + + if (fips140_wait_until_ready(alg)) + WRITE_ONCE(alg->cra_init, cra_init); + return cra_init ? cra_init(tfm) : 0; +} + +static int fips140_aead_init_tfm(struct crypto_aead *tfm) +{ + struct aead_alg *alg = crypto_aead_alg(tfm); + int (*init)(struct crypto_aead *tfm) = + fips140_load_init_function(&alg->base); + + if (fips140_wait_until_ready(&alg->base)) + WRITE_ONCE(alg->init, init); + return init ? init(tfm) : 0; +} + +static int fips140_ahash_init_tfm(struct crypto_ahash *tfm) +{ + struct hash_alg_common *halg = crypto_hash_alg_common(tfm); + struct ahash_alg *alg = container_of(halg, struct ahash_alg, halg); + int (*init_tfm)(struct crypto_ahash *tfm) = + fips140_load_init_function(&halg->base); + + if (fips140_wait_until_ready(&halg->base)) + WRITE_ONCE(alg->init_tfm, init_tfm); + return init_tfm ? init_tfm(tfm) : 0; +} + +static int fips140_shash_init_tfm(struct crypto_shash *tfm) +{ + struct shash_alg *alg = crypto_shash_alg(tfm); + int (*init_tfm)(struct crypto_shash *tfm) = + fips140_load_init_function(&alg->base); + + if (fips140_wait_until_ready(&alg->base)) + WRITE_ONCE(alg->init_tfm, init_tfm); + return init_tfm ? init_tfm(tfm) : 0; +} + +static int fips140_skcipher_init_tfm(struct crypto_skcipher *tfm) +{ + struct skcipher_alg *alg = crypto_skcipher_alg(tfm); + int (*init)(struct crypto_skcipher *tfm) = + fips140_load_init_function(&alg->base); + + if (fips140_wait_until_ready(&alg->base)) + WRITE_ONCE(alg->init, init); + return init ? init(tfm) : 0; +} + +/* Single algorithm registration */ + +#define prepare_alg(alg, base_alg, field, wrapper_func) \ +({ \ + int err = 0; \ + \ + if (!fips140_ready() && alg->field != wrapper_func) { \ + err = fips140_store_init_function(base_alg, alg->field);\ + if (err == 0) \ + alg->field = wrapper_func; \ + } \ + err; \ +}) + +static int fips140_prepare_alg(struct crypto_alg *alg) +{ + /* + * Override cra_init. This is only for algorithm types like cipher and + * rng that don't have a strongly-typed initialization function. + */ + return prepare_alg(alg, alg, cra_init, fips140_alg_init_tfm); +} + +static int fips140_prepare_aead_alg(struct aead_alg *alg) +{ + return prepare_alg(alg, &alg->base, init, fips140_aead_init_tfm); +} + +static int fips140_prepare_ahash_alg(struct ahash_alg *alg) +{ + return prepare_alg(alg, &alg->halg.base, init_tfm, + fips140_ahash_init_tfm); +} + +static int fips140_prepare_rng_alg(struct rng_alg *alg) +{ + /* + * rng doesn't have a strongly-typed initialization function, so we must + * treat rng algorithms as "generic" algorithms. + */ + return fips140_prepare_alg(&alg->base); +} + +static int fips140_prepare_shash_alg(struct shash_alg *alg) +{ + return prepare_alg(alg, &alg->base, init_tfm, fips140_shash_init_tfm); +} + +static int fips140_prepare_skcipher_alg(struct skcipher_alg *alg) +{ + return prepare_alg(alg, &alg->base, init, fips140_skcipher_init_tfm); +} + +int fips140_crypto_register_alg(struct crypto_alg *alg) +{ + return fips140_prepare_alg(alg) ?: crypto_register_alg(alg); +} + +int fips140_crypto_register_aead(struct aead_alg *alg) +{ + return fips140_prepare_aead_alg(alg) ?: crypto_register_aead(alg); +} + +int fips140_crypto_register_ahash(struct ahash_alg *alg) +{ + return fips140_prepare_ahash_alg(alg) ?: crypto_register_ahash(alg); +} + +int fips140_crypto_register_rng(struct rng_alg *alg) +{ + return fips140_prepare_rng_alg(alg) ?: crypto_register_rng(alg); +} + +int fips140_crypto_register_shash(struct shash_alg *alg) +{ + return fips140_prepare_shash_alg(alg) ?: crypto_register_shash(alg); +} + +int fips140_crypto_register_skcipher(struct skcipher_alg *alg) +{ + return fips140_prepare_skcipher_alg(alg) ?: + crypto_register_skcipher(alg); +} + +/* Instance registration */ + +int fips140_aead_register_instance(struct crypto_template *tmpl, + struct aead_instance *inst) +{ + return fips140_prepare_aead_alg(&inst->alg) ?: + aead_register_instance(tmpl, inst); +} + +int fips140_ahash_register_instance(struct crypto_template *tmpl, + struct ahash_instance *inst) +{ + return fips140_prepare_ahash_alg(&inst->alg) ?: + ahash_register_instance(tmpl, inst); +} + +int fips140_shash_register_instance(struct crypto_template *tmpl, + struct shash_instance *inst) +{ + return fips140_prepare_shash_alg(&inst->alg) ?: + shash_register_instance(tmpl, inst); +} + +int fips140_skcipher_register_instance(struct crypto_template *tmpl, + struct skcipher_instance *inst) +{ + return fips140_prepare_skcipher_alg(&inst->alg) ?: + skcipher_register_instance(tmpl, inst); +} + +/* Bulk algorithm registration */ + +int fips140_crypto_register_algs(struct crypto_alg *algs, int count) +{ + int i; + int err; + + for (i = 0; i < count; i++) { + err = fips140_prepare_alg(&algs[i]); + if (err) + return err; + } + + return crypto_register_algs(algs, count); +} + +int fips140_crypto_register_aeads(struct aead_alg *algs, int count) +{ + int i; + int err; + + for (i = 0; i < count; i++) { + err = fips140_prepare_aead_alg(&algs[i]); + if (err) + return err; + } + + return crypto_register_aeads(algs, count); +} + +int fips140_crypto_register_ahashes(struct ahash_alg *algs, int count) +{ + int i; + int err; + + for (i = 0; i < count; i++) { + err = fips140_prepare_ahash_alg(&algs[i]); + if (err) + return err; + } + + return crypto_register_ahashes(algs, count); +} + +int fips140_crypto_register_rngs(struct rng_alg *algs, int count) +{ + int i; + int err; + + for (i = 0; i < count; i++) { + err = fips140_prepare_rng_alg(&algs[i]); + if (err) + return err; + } + + return crypto_register_rngs(algs, count); +} + +int fips140_crypto_register_shashes(struct shash_alg *algs, int count) +{ + int i; + int err; + + for (i = 0; i < count; i++) { + err = fips140_prepare_shash_alg(&algs[i]); + if (err) + return err; + } + + return crypto_register_shashes(algs, count); +} + +int fips140_crypto_register_skciphers(struct skcipher_alg *algs, int count) +{ + int i; + int err; + + for (i = 0; i < count; i++) { + err = fips140_prepare_skcipher_alg(&algs[i]); + if (err) + return err; + } + + return crypto_register_skciphers(algs, count); +} diff --git a/crypto/fips140-defs.h b/crypto/fips140-defs.h new file mode 100644 index 000000000000..d3f62e3c3f37 --- /dev/null +++ b/crypto/fips140-defs.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2021 Google LLC + * + * This file is automatically included by all files built into fips140.ko, via + * the "-include" compiler flag. + */ + +/* + * fips140.ko is built from various unmodified or minimally modified kernel + * source files, many of which are normally meant to be buildable into different + * modules themselves. That results in conflicting instances of module_init() + * and related macros such as MODULE_LICENSE(). + * + * To solve that, we undefine MODULE to trick the kernel headers into thinking + * the code is being compiled as built-in. That causes module_init() and + * related macros to be expanded as they would be for built-in code; e.g., + * module_init() adds the function to the .initcalls section of the binary. + * + * The .c files that contain the real module_init, module license, and module + * parameters for fips140.ko are then responsible for redefining MODULE. The + * real module_init executes all initcalls that were collected into .initcalls. + */ +#undef MODULE + +/* + * Defining KBUILD_MODFILE is also required, since the kernel headers expect it + * to be defined when code that can be a module is compiled as built-in. + */ +#define KBUILD_MODFILE "crypto/fips140" + +/* + * Disable symbol exports by default. fips140.ko includes various files that + * use EXPORT_SYMBOL*(), but it's unwanted to export any symbols from fips140.ko + * except where explicitly needed for FIPS certification reasons. + */ +#define __DISABLE_EXPORTS + +/* + * Redirect all calls to algorithm registration functions to the wrapper + * functions defined within the module. + */ +#define aead_register_instance fips140_aead_register_instance +#define ahash_register_instance fips140_ahash_register_instance +#define crypto_register_aead fips140_crypto_register_aead +#define crypto_register_aeads fips140_crypto_register_aeads +#define crypto_register_ahash fips140_crypto_register_ahash +#define crypto_register_ahashes fips140_crypto_register_ahashes +#define crypto_register_alg fips140_crypto_register_alg +#define crypto_register_algs fips140_crypto_register_algs +#define crypto_register_rng fips140_crypto_register_rng +#define crypto_register_rngs fips140_crypto_register_rngs +#define crypto_register_shash fips140_crypto_register_shash +#define crypto_register_shashes fips140_crypto_register_shashes +#define crypto_register_skcipher fips140_crypto_register_skcipher +#define crypto_register_skciphers fips140_crypto_register_skciphers +#define shash_register_instance fips140_shash_register_instance +#define skcipher_register_instance fips140_skcipher_register_instance diff --git a/crypto/fips140-eval-testing-uapi.h b/crypto/fips140-eval-testing-uapi.h new file mode 100644 index 000000000000..04e6cf633594 --- /dev/null +++ b/crypto/fips140-eval-testing-uapi.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +#ifndef _CRYPTO_FIPS140_EVAL_TESTING_H +#define _CRYPTO_FIPS140_EVAL_TESTING_H + +#include + +/* + * This header defines the ioctls that are available on the fips140 character + * device. These ioctls expose some of the module's services to userspace so + * that they can be tested by the FIPS certification lab; this is a required + * part of getting a FIPS 140 certification. These ioctls do not have any other + * purpose, and they do not need to be present in production builds. + */ + +/* + * Call the fips140_is_approved_service() function. The argument must be the + * service name as a NUL-terminated string. The return value will be 1 if + * fips140_is_approved_service() returned true, or 0 if it returned false. + */ +#define FIPS140_IOCTL_IS_APPROVED_SERVICE _IO('F', 0) + +/* + * Call the fips140_module_version() function. The argument must be a pointer + * to a buffer of size >= 256 chars. The NUL-terminated string returned by + * fips140_module_version() will be written to this buffer. + */ +#define FIPS140_IOCTL_MODULE_VERSION _IOR('F', 1, char[256]) + +#endif /* _CRYPTO_FIPS140_EVAL_TESTING_H */ diff --git a/crypto/fips140-eval-testing.c b/crypto/fips140-eval-testing.c new file mode 100644 index 000000000000..fdc786884d11 --- /dev/null +++ b/crypto/fips140-eval-testing.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2021 Google LLC + * + * This file can optionally be built into fips140.ko in order to support certain + * types of testing that the FIPS lab has to do to evaluate the module. It + * should not be included in production builds of the module. + */ + +/* + * We have to redefine inline to mean always_inline, so that _copy_to_user() + * gets inlined. This is needed for it to be placed into the correct section. + * See fips140_copy_to_user(). + * + * We also need to undefine BUILD_FIPS140_KO to allow the use of the code + * patching which copy_to_user() requires. + */ +#undef inline +#define inline inline __attribute__((__always_inline__)) __gnu_inline \ + __inline_maybe_unused notrace +#undef BUILD_FIPS140_KO + +/* + * Since this .c file contains real module parameters for fips140.ko, it needs + * to be compiled normally, so undo the hacks that were done in fips140-defs.h. + */ +#define MODULE +#undef KBUILD_MODFILE +#undef __DISABLE_EXPORTS + +#include +#include +#include +#include + +#include "fips140-module.h" +#include "fips140-eval-testing-uapi.h" + +/* + * This option allows deliberately failing the self-tests for a particular + * algorithm. + */ +static char *fips140_fail_selftest; +module_param_named(fail_selftest, fips140_fail_selftest, charp, 0); + +/* This option allows deliberately failing the integrity check. */ +static bool fips140_fail_integrity_check; +module_param_named(fail_integrity_check, fips140_fail_integrity_check, bool, 0); + +static dev_t fips140_devnum; +static struct cdev fips140_cdev; + +/* Inject a self-test failure (via corrupting the result) if requested. */ +void fips140_inject_selftest_failure(const char *impl, u8 *result) +{ + if (fips140_fail_selftest && strcmp(impl, fips140_fail_selftest) == 0) + result[0] ^= 0xff; +} + +/* Inject an integrity check failure (via corrupting the text) if requested. */ +void fips140_inject_integrity_failure(u8 *textcopy) +{ + if (fips140_fail_integrity_check) + textcopy[0] ^= 0xff; +} + +static long fips140_ioctl_is_approved_service(unsigned long arg) +{ + const char *service_name = strndup_user((const char __user *)arg, 256); + long ret; + + if (IS_ERR(service_name)) + return PTR_ERR(service_name); + + ret = fips140_is_approved_service(service_name); + + kfree(service_name); + return ret; +} + +/* + * Code in fips140.ko is covered by an integrity check by default, and this + * check breaks if copy_to_user() is called. This is because copy_to_user() is + * an inline function that relies on code patching. However, since this is + * "evaluation testing" code which isn't included in the production builds of + * fips140.ko, it's acceptable to just exclude it from the integrity check. + */ +static noinline unsigned long __section("text.._fips140_unchecked") +fips140_copy_to_user(void __user *to, const void *from, unsigned long n) +{ + return copy_to_user(to, from, n); +} + +static long fips140_ioctl_module_version(unsigned long arg) +{ + const char *version = fips140_module_version(); + size_t len = strlen(version) + 1; + + if (len > 256) + return -EOVERFLOW; + + if (fips140_copy_to_user((void __user *)arg, version, len)) + return -EFAULT; + + return 0; +} + +static long fips140_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case FIPS140_IOCTL_IS_APPROVED_SERVICE: + return fips140_ioctl_is_approved_service(arg); + case FIPS140_IOCTL_MODULE_VERSION: + return fips140_ioctl_module_version(arg); + default: + return -ENOTTY; + } +} + +static const struct file_operations fips140_fops = { + .unlocked_ioctl = fips140_ioctl, +}; + +bool fips140_eval_testing_init(void) +{ + if (alloc_chrdev_region(&fips140_devnum, 1, 1, "fips140") != 0) { + pr_err("failed to allocate device number\n"); + return false; + } + cdev_init(&fips140_cdev, &fips140_fops); + if (cdev_add(&fips140_cdev, fips140_devnum, 1) != 0) { + pr_err("failed to add fips140 character device\n"); + return false; + } + return true; +} diff --git a/crypto/fips140-generated-testvecs.h b/crypto/fips140-generated-testvecs.h new file mode 100644 index 000000000000..aaf140958d62 --- /dev/null +++ b/crypto/fips140-generated-testvecs.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2021 Google LLC */ + +/* + * This header was automatically generated by gen_fips140_testvecs.py. + * Don't edit it directly. + */ + +static const u8 fips_message[32] __initconst = + "This is a 32-byte test message."; + +static const u8 fips_aes_key[16] __initconst = "128-bit AES key"; + +static const u8 fips_aes_iv[16] __initconst = "ABCDEFGHIJKLMNOP"; + +static const u8 fips_aes_cbc_ciphertext[32] __initconst = + "\x4c\x3e\xeb\x38\x8d\x1f\x28\xfd\xa2\x3b\xa9\xda\x36\xf2\x99\xe2" + "\x84\x84\x66\x37\x0a\x53\x68\x2f\x17\x95\x8d\x7f\xca\x5a\x68\x4e"; + +static const u8 fips_aes_ecb_ciphertext[32] __initconst = + "\xc1\x9d\xe6\xb8\xb2\x90\xff\xfe\xf2\x77\x18\xb0\x55\xd3\xee\xa9" + "\xe2\x6f\x4a\x32\x67\xfd\xb7\xa5\x2f\x4b\x6e\x1a\x86\x2b\x6e\x3a"; + +static const u8 fips_aes_ctr_ciphertext[32] __initconst = + "\xed\x06\x2c\xd0\xbc\x48\xd1\x2e\x6a\x4e\x13\xe9\xaa\x17\x40\xca" + "\x00\xb4\xaf\x3b\x4f\xee\x73\xd6\x6c\x41\xf6\x4c\x8b\x0d\x6a\x0f"; + +static const u8 fips_aes_gcm_assoc[22] __initconst = "associated data string"; + +static const u8 fips_aes_gcm_ciphertext[48] __initconst = + "\x37\x88\x3e\x1d\x58\x50\xda\x10\x07\xeb\x52\xdf\xea\x0a\x54\xd4" + "\x44\xbf\x88\x2a\xf3\x03\x03\x84\xaf\x8b\x96\xbd\xea\x65\x60\x6f" + "\x82\xfa\x51\xf4\x28\xad\x0c\xf1\xce\x0f\x91\xdd\x1a\x4c\x77\x5f"; + +static const u8 fips_aes_xts_key[32] __initconst = + "This is an AES-128-XTS key."; + +static const u8 fips_aes_xts_ciphertext[32] __initconst = + "\x4f\xf7\x9f\x6c\x00\xa8\x30\xdf\xff\xf3\x25\x9c\xf6\x0b\x1b\xfd" + "\x3b\x34\x5e\x67\x7c\xf8\x8b\x68\x9a\xb9\x5a\x89\x51\x51\xbd\x35"; + +static const u8 fips_aes_cmac_digest[16] __initconst = + "\x0c\x05\xda\x64\x51\x0c\x8e\x6c\x86\x52\x46\xa8\x2d\xb1\xfe\x0f"; + +static const u8 fips_hmac_key[16] __initconst = "128-bit HMAC key"; + +static const u8 fips_sha1_digest[20] __initconst = + "\x1b\x78\xc7\x4b\xd5\xd4\x83\xb1\x58\xc5\x96\x83\x4f\x16\x8d\x15" + "\xb4\xaa\x22\x8c"; + +static const u8 fips_sha256_digest[32] __initconst = + "\x4e\x11\x83\x0c\x53\x80\x1e\x5f\x9b\x38\x33\x38\xe8\x74\x43\xb0" + "\xc1\x3a\xbe\xbf\x75\xf0\x12\x0f\x21\x33\xf5\x16\x33\xf1\xb0\x81"; + +static const u8 fips_hmac_sha256_digest[32] __initconst = + "\x63\x0e\xb5\x73\x79\xfc\xaf\x5f\x86\xe3\xaf\xf0\xc8\x36\xef\xd5" + "\x35\x8d\x40\x25\x38\xb3\x65\x72\x98\xf3\x59\xd8\x1e\x54\x4c\xa1"; + +static const u8 fips_sha512_digest[64] __initconst = + "\x32\xe0\x44\x23\xbd\xe3\xec\x28\xbf\xf1\x34\x11\xd5\xae\xbf\xd5" + "\xc0\x8e\xb5\xa1\x04\xef\x2f\x07\x84\xf1\xd9\x83\x0f\x6c\x31\xab" + "\xf7\xe7\x57\xfa\xf7\xae\xf0\x6f\xb2\x16\x08\x32\xcf\xc7\xef\x35" + "\xb3\x3b\x51\xb9\xfd\xe7\xff\x5e\xb2\x8b\xc6\x79\xe6\x14\x04\xb4"; + +static const u8 fips_sha3_256_digest[32] __initconst = + "\xe5\x9f\x8a\x6d\x87\xbe\x4a\x76\x0a\xcd\xe3\x7e\x89\x28\xf6\xd3" + "\xbc\x55\x0e\x5c\xf0\xec\x59\x33\xad\x9b\x9f\x09\x44\xa3\x88\xb8"; + +/* + * This header was automatically generated by gen_fips140_testvecs.py. + * Don't edit it directly. + */ diff --git a/crypto/fips140-module.c b/crypto/fips140-module.c new file mode 100644 index 000000000000..819b4bf63395 --- /dev/null +++ b/crypto/fips140-module.c @@ -0,0 +1,817 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2021 Google LLC + * Author: Ard Biesheuvel + * + * This file is the core of fips140.ko, which contains various crypto algorithms + * that are also built into vmlinux. At load time, this module overrides the + * built-in implementations of these algorithms with its implementations. It + * also runs self-tests on these algorithms and verifies the integrity of its + * code and data. If either of these steps fails, the kernel will panic. + * + * This module is intended to be loaded at early boot time in order to meet + * FIPS 140 and NIAP FPT_TST_EXT.1 requirements. It shouldn't be used if you + * don't need to meet these requirements. + */ + +/* + * Since this .c file is the real entry point of fips140.ko, it needs to be + * compiled normally, so undo the hacks that were done in fips140-defs.h. + */ +#define MODULE +#undef KBUILD_MODFILE +#undef __DISABLE_EXPORTS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fips140-module.h" +#include "internal.h" + +/* + * FIPS 140-2 prefers the use of HMAC with a public key over a plain hash. + */ +u8 __initdata fips140_integ_hmac_key[] = "The quick brown fox jumps over the lazy dog"; + +/* this is populated by the build tool */ +u8 __initdata fips140_integ_hmac_digest[SHA256_DIGEST_SIZE]; + +const initcall_entry_t __fips140_initcalls_start __section(".initcalls._start"); +const initcall_entry_t __fips140_initcalls_end __section(".initcalls._end"); + +const u8 __fips140_text_start __section(".text.._start"); +const u8 __fips140_text_end __section(".text.._end"); + +const u8 __fips140_rodata_start __section(".rodata.._start"); +const u8 __fips140_rodata_end __section(".rodata.._end"); + +/* + * We need this little detour to prevent Clang from detecting out of bounds + * accesses to the above *_start symbols, which exist only to delineate the + * corresponding sections, and so their sizes are not relevant to us. + */ +const initcall_entry_t *fips140_initcalls_start = &__fips140_initcalls_start; +const u8 *fips140_text_start = &__fips140_text_start; +const u8 *fips140_rodata_start = &__fips140_rodata_start; + +/* + * fips140_algs[] lists the algorithms that this module unregisters from the + * kernel crypto API so that it can register its own implementation(s) of them. + * + * There are two reasons to do the unregistration, i.e. replace the kernel's + * algorithms instead of just adding more algorithms. First, the kernel crypto + * API doesn't allow algorithms with duplicate driver names. Second, for FIPS + * approved algorithms we have to ensure that the FIPS copies are actually used. + */ +static struct fips140_alg { + /* + * Either cra_name or cra_driver_name is set. + * + * cra_name makes the entry match all software implementations of a + * given algorithm. This is used when the module is meant to replace + * *all* software implementations of the algorithm. This is required + * for FIPS approved algorithms (approved=true). When using this style + * of matching, it must be ensured that the module contains all the same + * implementations of the algorithm as the kernel itself; otherwise the + * kernel's functionality and/or performance could be impacted by the + * insertion of the fips140 module. + * + * cra_driver_name makes the entry match a single implementation of an + * algorithm. This is used for some specific non FIPS approved + * algorithm implementations that get pulled in by being located in the + * same source files as implementations of FIPS approved algorithms. + */ + const char *cra_name; + const char *cra_driver_name; + + /* + * approved is true if fips140_is_approved_service() should return that + * the algorithm is approved. This requires cra_name != NULL. + */ + bool approved; + + /* + * maybe_uninstantiated is true if the module provides this algorithm + * but doesn't register it directly at module initialization time. This + * occurs for some of the HMAC variants because they are provided by a + * template which isn't immediately instantiated for every SHA variant + * (since the HMAC self-test only has to test one SHA variant). + */ + bool maybe_uninstantiated; + + /* + * unregistered_inkern gets set to true at runtime if at least one + * algorithm matching this entry was unregistered from the kernel. This + * is used to detect unregistrations with no matching registration. + */ + bool unregistered_inkern; +} fips140_algs[] = { + /* Approved algorithms, all specified by cra_name */ + { .cra_name = "aes", .approved = true }, + { .cra_name = "cbc(aes)", .approved = true }, + { .cra_name = "cmac(aes)", .approved = true }, + { .cra_name = "ctr(aes)", .approved = true }, + { .cra_name = "cts(cbc(aes))", .approved = true }, + { .cra_name = "ecb(aes)", .approved = true }, + { .cra_name = "hmac(sha1)", .approved = true, + .maybe_uninstantiated = true }, + { .cra_name = "hmac(sha224)", .approved = true, + .maybe_uninstantiated = true }, + { .cra_name = "hmac(sha256)", .approved = true }, + { .cra_name = "hmac(sha384)", .approved = true, + .maybe_uninstantiated = true }, + { .cra_name = "hmac(sha512)", .approved = true, + .maybe_uninstantiated = true }, + { .cra_name = "sha1", .approved = true }, + { .cra_name = "sha224", .approved = true }, + { .cra_name = "sha256", .approved = true }, + { .cra_name = "sha384", .approved = true }, + { .cra_name = "sha512", .approved = true }, + { .cra_name = "sha3-224", .approved = true }, + { .cra_name = "sha3-256", .approved = true }, + { .cra_name = "sha3-384", .approved = true }, + { .cra_name = "sha3-512", .approved = true }, + { .cra_name = "stdrng", .approved = true }, + { .cra_name = "xts(aes)", .approved = true }, + + /* + * Non-approved algorithms specified by cra_name. + * + * Due to a quirk in the FIPS requirements, AES-GCM can't be FIPS + * approved. But we treat it the same as approved algorithms in that we + * ensure that a self-test and all needed implementations are included. + * + * The Jitter RNG is needed in the module as an entropy source for the + * DRBG algorithms, but it's not considered to be approved itself. + */ + { .cra_name = "gcm(aes)" }, + { .cra_name = "jitterentropy_rng" }, + + /* Non-approved algorithms specified by cra_driver_name */ + { .cra_driver_name = "essiv-cbc-aes-sha256-ce" }, + { .cra_driver_name = "essiv-cbc-aes-sha256-neon" }, + { .cra_driver_name = "cbcmac-aes-ce" }, + { .cra_driver_name = "cbcmac-aes-neon" }, + { .cra_driver_name = "rfc4106-gcm-aes-ce" }, + { .cra_driver_name = "xcbc-aes-ce" }, + { .cra_driver_name = "xcbc-aes-neon" }, + { .cra_driver_name = "xctr-aes-ce" }, + { .cra_driver_name = "xctr-aes-neon" }, +}; + +/* + * Return true if the crypto API algorithm @calg is matched by the fips140 + * module algorithm specification @falg. + */ +static bool __init fips140_alg_matches(const struct fips140_alg *falg, + const struct crypto_alg *calg) +{ + /* + * All software algorithms are synchronous. Hardware algorithms must be + * covered by their own FIPS 140 certification. + */ + if (calg->cra_flags & CRYPTO_ALG_ASYNC) + return false; + + if (falg->cra_name != NULL && + strcmp(falg->cra_name, calg->cra_name) == 0) + return true; + + if (falg->cra_driver_name != NULL && + strcmp(falg->cra_driver_name, calg->cra_driver_name) == 0) + return true; + + return false; +} + +/* Find the entry in fips140_algs[], if any, that @calg is matched by. */ +static struct fips140_alg *__init +fips140_find_matching_alg(const struct crypto_alg *calg) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(fips140_algs); i++) { + if (fips140_alg_matches(&fips140_algs[i], calg)) + return &fips140_algs[i]; + } + return NULL; +} + +/* + * FIPS 140-3 service indicators. FIPS 140-3 requires that all services + * "provide an indicator when the service utilises an approved cryptographic + * algorithm, security function or process in an approved manner". What this + * means is very debatable, even with the help of the FIPS 140-3 Implementation + * Guidance document. However, it was decided that a function that takes in an + * algorithm name and returns whether that algorithm is approved or not will + * meet this requirement. Note, this relies on some properties of the module: + * + * - The module doesn't distinguish between "services" and "algorithms"; its + * services are simply its algorithms. + * + * - The status of an approved algorithm is never non-approved, since (a) the + * module doesn't support operating in a non-approved mode, such as a mode + * where the self-tests are skipped; (b) there are no cases where the module + * supports non-approved settings for approved algorithms, e.g. + * non-approved key sizes; and (c) this function isn't available to be + * called until the module_init function has completed, so it's guaranteed + * that the self-tests and integrity check have already passed. + * + * - The module does support some non-approved algorithms, so a single static + * indicator ("return true;") would not be acceptable. + */ +bool fips140_is_approved_service(const char *name) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(fips140_algs); i++) { + if (fips140_algs[i].approved && + fips140_algs[i].cra_name != NULL && + strcmp(name, fips140_algs[i].cra_name) == 0) + return true; + } + return false; +} +EXPORT_SYMBOL_GPL(fips140_is_approved_service); + +/* + * FIPS 140-3 requires that modules provide a "service" that outputs "the name + * or module identifier and the versioning information that can be correlated + * with a validation record". This function meets that requirement. + * + * Note: the module also prints this same information to the kernel log when it + * is loaded. That might meet the requirement by itself. However, given the + * vagueness of what counts as a "service", we provide this function too, just + * in case the certification lab or CMVP is happier with an explicit function. + * + * Note: /sys/modules/fips140/scmversion also provides versioning information + * about the module. However that file just shows the bare git commit ID, so it + * probably isn't sufficient to meet the FIPS requirement, which seems to want + * the "official" module name and version number used in the FIPS certificate. + */ +const char *fips140_module_version(void) +{ + return FIPS140_MODULE_NAME " " FIPS140_MODULE_VERSION; +} +EXPORT_SYMBOL_GPL(fips140_module_version); + +static LIST_HEAD(existing_live_algos); + +/* + * Release a list of algorithms which have been removed from crypto_alg_list. + * + * Note that even though the list is a private list, we have to hold + * crypto_alg_sem while iterating through it because crypto_unregister_alg() may + * run concurrently (as we haven't taken a reference to the algorithms on the + * list), and crypto_unregister_alg() will remove the algorithm from whichever + * list it happens to be on, while holding crypto_alg_sem. That's okay, since + * in that case crypto_unregister_alg() will handle the crypto_alg_put(). + */ +static void fips140_remove_final(struct list_head *list) +{ + struct crypto_alg *alg; + struct crypto_alg *n; + + /* + * We need to take crypto_alg_sem to safely traverse the list (see + * comment above), but we have to drop it when doing each + * crypto_alg_put() as that may take crypto_alg_sem again. + */ + down_write(&crypto_alg_sem); + list_for_each_entry_safe(alg, n, list, cra_list) { + list_del_init(&alg->cra_list); + up_write(&crypto_alg_sem); + + crypto_alg_put(alg); + + down_write(&crypto_alg_sem); + } + up_write(&crypto_alg_sem); +} + +static void __init unregister_existing_fips140_algos(void) +{ + struct crypto_alg *calg, *tmp; + LIST_HEAD(remove_list); + LIST_HEAD(spawns); + + down_write(&crypto_alg_sem); + + /* + * Find all registered algorithms that we care about, and move them to a + * private list so that they are no longer exposed via the algo lookup + * API. Subsequently, we will unregister them if they are not in active + * use. If they are, we can't fully unregister them but we can ensure + * that new users won't use them. + */ + list_for_each_entry_safe(calg, tmp, &crypto_alg_list, cra_list) { + struct fips140_alg *falg = fips140_find_matching_alg(calg); + + if (!falg) + continue; + falg->unregistered_inkern = true; + + if (refcount_read(&calg->cra_refcnt) == 1) { + /* + * This algorithm is not currently in use, but there may + * be template instances holding references to it via + * spawns. So let's tear it down like + * crypto_unregister_alg() would, but without releasing + * the lock, to prevent races with concurrent TFM + * allocations. + */ + calg->cra_flags |= CRYPTO_ALG_DEAD; + list_move(&calg->cra_list, &remove_list); + crypto_remove_spawns(calg, &spawns, NULL); + } else { + /* + * This algorithm is live, i.e. it has TFMs allocated, + * so we can't fully unregister it. It's not necessary + * to dynamically redirect existing users to the FIPS + * code, given that they can't be relying on FIPS + * certified crypto in the first place. However, we do + * need to ensure that new users will get the FIPS code. + * + * In most cases, setting calg->cra_priority to 0 + * achieves this. However, that isn't enough for + * algorithms like "hmac(sha256)" that need to be + * instantiated from a template, since existing + * algorithms always take priority over a template being + * instantiated. Therefore, we move the algorithm to + * a private list so that algorithm lookups won't find + * it anymore. To further distinguish it from the FIPS + * algorithms, we also append "+orig" to its name. + */ + pr_info("found already-live algorithm '%s' ('%s')\n", + calg->cra_name, calg->cra_driver_name); + calg->cra_priority = 0; + strlcat(calg->cra_name, "+orig", CRYPTO_MAX_ALG_NAME); + strlcat(calg->cra_driver_name, "+orig", + CRYPTO_MAX_ALG_NAME); + list_move(&calg->cra_list, &existing_live_algos); + } + } + up_write(&crypto_alg_sem); + + fips140_remove_final(&remove_list); + fips140_remove_final(&spawns); +} + +/* + * The algorithms unregistered by fips140.ko are determined by fips140_algs[], + * but the algorithms registered by fips140.ko are determined by its initcalls. + * There is a chance these get out of sync. Therefore, this function detects + * cases where an algorithm was unregistered without a replacement being + * registered. It returns true if things look ok or false if there's a problem. + */ +static bool __init fips140_verify_no_extra_unregistrations(void) +{ + bool ok = true; + size_t i; + + down_read(&crypto_alg_sem); + for (i = 0; i < ARRAY_SIZE(fips140_algs); i++) { + const struct fips140_alg *falg = &fips140_algs[i]; + const struct crypto_alg *calg; + bool registered = false; + + if (falg->maybe_uninstantiated || !falg->unregistered_inkern) + continue; + + list_for_each_entry(calg, &crypto_alg_list, cra_list) { + if (fips140_alg_matches(falg, calg)) { + registered = true; + break; + } + } + if (!registered) { + pr_err("This module unregistered %s but did not replace it!\n", + falg->cra_name ?: falg->cra_driver_name); + pr_err("Either remove it from fips140_algs[], or fix the module to include it.\n"); + ok = false; + } + } + up_read(&crypto_alg_sem); + return ok; +} + +static void __init unapply_text_relocations(void *section, int section_size, + const Elf64_Rela *rela, int numrels) +{ + while (numrels--) { + u32 *place = (u32 *)(section + rela->r_offset); + + BUG_ON(rela->r_offset >= section_size); + + switch (ELF64_R_TYPE(rela->r_info)) { +#ifdef CONFIG_ARM64 + case R_AARCH64_ABS32: /* for KCFI */ + *place = 0; + break; + + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: + *place &= ~GENMASK(25, 0); + break; + + case R_AARCH64_ADR_PREL_LO21: + case R_AARCH64_ADR_PREL_PG_HI21: + case R_AARCH64_ADR_PREL_PG_HI21_NC: + *place &= ~(GENMASK(30, 29) | GENMASK(23, 5)); + break; + + case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_LDST16_ABS_LO12_NC: + case R_AARCH64_LDST32_ABS_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: + case R_AARCH64_LDST128_ABS_LO12_NC: + *place &= ~GENMASK(21, 10); + break; + default: + pr_err("unhandled relocation type %llu\n", + ELF64_R_TYPE(rela->r_info)); + BUG(); +#else +#error +#endif + } + rela++; + } +} + +static void __init unapply_rodata_relocations(void *section, int section_size, + const Elf64_Rela *rela, int numrels) +{ + while (numrels--) { + void *place = section + rela->r_offset; + + BUG_ON(rela->r_offset >= section_size); + + switch (ELF64_R_TYPE(rela->r_info)) { +#ifdef CONFIG_ARM64 + case R_AARCH64_ABS64: + *(u64 *)place = 0; + break; + default: + pr_err("unhandled relocation type %llu\n", + ELF64_R_TYPE(rela->r_info)); + BUG(); +#else +#error +#endif + } + rela++; + } +} + +enum { + PACIASP = 0xd503233f, + AUTIASP = 0xd50323bf, + SCS_PUSH = 0xf800865e, + SCS_POP = 0xf85f8e5e, +}; + +/* + * To make the integrity check work with dynamic Shadow Call Stack (SCS), + * replace all instructions that push or pop from the SCS with the Pointer + * Authentication Code (PAC) instructions that were present originally. + */ +static void __init unapply_scs_patch(void *section, int section_size) +{ +#if defined(CONFIG_ARM64) && defined(CONFIG_UNWIND_PATCH_PAC_INTO_SCS) + u32 *insns = section; + int i; + + for (i = 0; i < section_size / sizeof(insns[0]); i++) { + if (insns[i] == SCS_PUSH) + insns[i] = PACIASP; + else if (insns[i] == SCS_POP) + insns[i] = AUTIASP; + } +#endif +} + +#ifdef CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK +static struct { + const void *text; + int textsize; + const void *rodata; + int rodatasize; +} saved_integrity_check_info; + +static ssize_t fips140_text_read(struct file *file, char __user *to, + size_t count, loff_t *ppos) +{ + return simple_read_from_buffer(to, count, ppos, + saved_integrity_check_info.text, + saved_integrity_check_info.textsize); +} + +static ssize_t fips140_rodata_read(struct file *file, char __user *to, + size_t count, loff_t *ppos) +{ + return simple_read_from_buffer(to, count, ppos, + saved_integrity_check_info.rodata, + saved_integrity_check_info.rodatasize); +} + +static const struct file_operations fips140_text_fops = { + .read = fips140_text_read, +}; + +static const struct file_operations fips140_rodata_fops = { + .read = fips140_rodata_read, +}; + +static void fips140_init_integrity_debug_files(const void *text, int textsize, + const void *rodata, + int rodatasize) +{ + struct dentry *dir; + + dir = debugfs_create_dir("fips140", NULL); + + saved_integrity_check_info.text = kmemdup(text, textsize, GFP_KERNEL); + saved_integrity_check_info.textsize = textsize; + if (saved_integrity_check_info.text) + debugfs_create_file("text", 0400, dir, NULL, + &fips140_text_fops); + + saved_integrity_check_info.rodata = kmemdup(rodata, rodatasize, + GFP_KERNEL); + saved_integrity_check_info.rodatasize = rodatasize; + if (saved_integrity_check_info.rodata) + debugfs_create_file("rodata", 0400, dir, NULL, + &fips140_rodata_fops); +} +#else /* CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK */ +static void fips140_init_integrity_debug_files(const void *text, int textsize, + const void *rodata, + int rodatasize) +{ +} +#endif /* !CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK */ + +extern struct { + u32 offset; + u32 count; +} fips140_rela_text, fips140_rela_rodata; + +static bool __init check_fips140_module_hmac(void) +{ + struct crypto_shash *tfm = NULL; + SHASH_DESC_ON_STACK(desc, dontcare); + u8 digest[SHA256_DIGEST_SIZE]; + void *textcopy, *rodatacopy; + int textsize, rodatasize; + bool ok = false; + int err; + + textsize = &__fips140_text_end - &__fips140_text_start; + rodatasize = &__fips140_rodata_end - &__fips140_rodata_start; + + pr_info("text size : 0x%x\n", textsize); + pr_info("rodata size: 0x%x\n", rodatasize); + + textcopy = kmalloc(textsize + rodatasize, GFP_KERNEL); + if (!textcopy) { + pr_err("Failed to allocate memory for copy of .text\n"); + goto out; + } + + rodatacopy = textcopy + textsize; + + memcpy(textcopy, fips140_text_start, textsize); + memcpy(rodatacopy, fips140_rodata_start, rodatasize); + + // apply the relocations in reverse on the copies of .text and .rodata + unapply_text_relocations(textcopy, textsize, + offset_to_ptr(&fips140_rela_text.offset), + fips140_rela_text.count); + + unapply_rodata_relocations(rodatacopy, rodatasize, + offset_to_ptr(&fips140_rela_rodata.offset), + fips140_rela_rodata.count); + + unapply_scs_patch(textcopy, textsize); + + fips140_init_integrity_debug_files(textcopy, textsize, + rodatacopy, rodatasize); + + fips140_inject_integrity_failure(textcopy); + + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + if (IS_ERR(tfm)) { + pr_err("failed to allocate hmac tfm (%ld)\n", PTR_ERR(tfm)); + tfm = NULL; + goto out; + } + desc->tfm = tfm; + + pr_info("using '%s' for integrity check\n", + crypto_shash_driver_name(tfm)); + + err = crypto_shash_setkey(tfm, fips140_integ_hmac_key, + strlen(fips140_integ_hmac_key)) ?: + crypto_shash_init(desc) ?: + crypto_shash_update(desc, textcopy, textsize) ?: + crypto_shash_finup(desc, rodatacopy, rodatasize, digest); + + /* Zeroizing this is important; see the comment below. */ + shash_desc_zero(desc); + + if (err) { + pr_err("failed to calculate hmac shash (%d)\n", err); + goto out; + } + + if (memcmp(digest, fips140_integ_hmac_digest, sizeof(digest))) { + pr_err("provided_digest : %*phN\n", (int)sizeof(digest), + fips140_integ_hmac_digest); + + pr_err("calculated digest: %*phN\n", (int)sizeof(digest), + digest); + goto out; + } + ok = true; +out: + /* + * FIPS 140-3 requires that all "temporary value(s) generated during the + * integrity test" be zeroized (ref: FIPS 140-3 IG 9.7.B). There is no + * technical reason to do this given that these values are public + * information, but this is the requirement so we follow it. + */ + crypto_free_shash(tfm); + memzero_explicit(digest, sizeof(digest)); + kfree_sensitive(textcopy); + return ok; +} + +static void fips140_sha256(void *p, const u8 *data, unsigned int len, u8 *out, + int *hook_inuse) +{ + sha256(data, len, out); + *hook_inuse = 1; +} + +static void fips140_aes_expandkey(void *p, struct crypto_aes_ctx *ctx, + const u8 *in_key, unsigned int key_len, + int *err) +{ + *err = aes_expandkey(ctx, in_key, key_len); +} + +static void fips140_aes_encrypt(void *priv, const struct crypto_aes_ctx *ctx, + u8 *out, const u8 *in, int *hook_inuse) +{ + aes_encrypt(ctx, out, in); + *hook_inuse = 1; +} + +static void fips140_aes_decrypt(void *priv, const struct crypto_aes_ctx *ctx, + u8 *out, const u8 *in, int *hook_inuse) +{ + aes_decrypt(ctx, out, in); + *hook_inuse = 1; +} + +static bool update_fips140_library_routines(void) +{ + int ret; + + ret = register_trace_android_vh_sha256(fips140_sha256, NULL) ?: + register_trace_android_vh_aes_expandkey(fips140_aes_expandkey, NULL) ?: + register_trace_android_vh_aes_encrypt(fips140_aes_encrypt, NULL) ?: + register_trace_android_vh_aes_decrypt(fips140_aes_decrypt, NULL); + + return ret == 0; +} + +/* Initialize the FIPS 140 module */ +static int __init fips140_init(void) +{ + const initcall_entry_t *initcall; + + pr_info("loading " FIPS140_MODULE_NAME " " FIPS140_MODULE_VERSION "\n"); + fips140_init_thread = current; + + unregister_existing_fips140_algos(); + + /* iterate over all init routines present in this module and call them */ + for (initcall = fips140_initcalls_start + 1; + initcall < &__fips140_initcalls_end; + initcall++) { + initcall_t init = offset_to_ptr(initcall); + int err = init(); + + /* + * ENODEV is expected from initcalls that only register + * algorithms that depend on non-present CPU features. Besides + * that, errors aren't expected here. + */ + if (err && err != -ENODEV) { + pr_err("initcall %ps() failed: %d\n", init, err); + goto panic; + } + } + + if (!fips140_run_selftests()) + goto panic; + + if (!fips140_verify_no_extra_unregistrations()) + goto panic; + + /* + * It may seem backward to perform the integrity check last, but this + * is intentional: the check itself uses hmac(sha256) which is one of + * the algorithms that are replaced with versions from this module, and + * the integrity check must use the replacement version. Also, to be + * ready for FIPS 140-3, the integrity check algorithm must have already + * been self-tested. + */ + + if (!check_fips140_module_hmac()) { + if (!IS_ENABLED(CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK)) { + pr_crit("integrity check failed -- giving up!\n"); + goto panic; + } + pr_crit("ignoring integrity check failure due to debug mode\n"); + } else { + pr_info("integrity check passed\n"); + } + + complete_all(&fips140_tests_done); + + if (!update_fips140_library_routines()) + goto panic; + + if (!fips140_eval_testing_init()) + goto panic; + + pr_info("module successfully loaded\n"); + return 0; + +panic: + panic("FIPS 140 module load failure"); +} + +module_init(fips140_init); + +MODULE_IMPORT_NS(CRYPTO_INTERNAL); +MODULE_LICENSE("GPL v2"); + +/* + * Below are copies of some selected "crypto-related" helper functions that are + * used by fips140.ko but are not already built into it, due to them being + * defined in a file that cannot easily be built into fips140.ko (e.g., + * crypto/algapi.c) instead of one that can (e.g., most files in lib/). + * + * There is no hard rule about what needs to be included here, as this is for + * FIPS certifiability, not any technical reason. FIPS modules are supposed to + * implement the "crypto" themselves, but to do so they are allowed to call + * non-cryptographic helper functions from outside the module. Something like + * memcpy() is "clearly" non-cryptographic. However, there is is ambiguity + * about functions like crypto_inc() which aren't cryptographic by themselves, + * but are more closely associated with cryptography than e.g. memcpy(). To err + * on the side of caution, we define copies of some selected functions below so + * that calls to them from within fips140.ko will remain in fips140.ko. + */ + +static inline void crypto_inc_byte(u8 *a, unsigned int size) +{ + u8 *b = (a + size); + u8 c; + + for (; size; size--) { + c = *--b + 1; + *b = c; + if (c) + break; + } +} + +void crypto_inc(u8 *a, unsigned int size) +{ + __be32 *b = (__be32 *)(a + size); + u32 c; + + if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) || + IS_ALIGNED((unsigned long)b, __alignof__(*b))) + for (; size >= 4; size -= 4) { + c = be32_to_cpu(*--b) + 1; + *b = cpu_to_be32(c); + if (likely(c)) + return; + } + + crypto_inc_byte(a, size); +} diff --git a/crypto/fips140-module.h b/crypto/fips140-module.h new file mode 100644 index 000000000000..a2a63194eb64 --- /dev/null +++ b/crypto/fips140-module.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2021 Google LLC + */ + +#ifndef _CRYPTO_FIPS140_MODULE_H +#define _CRYPTO_FIPS140_MODULE_H + +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) "fips140: " fmt + +/* + * This is the name and version number of the module that are shown on the FIPS + * certificate. + */ +#define FIPS140_MODULE_NAME "Android Kernel Cryptographic Module" +#define FIPS140_MODULE_VERSION UTS_RELEASE + +/* fips140-eval-testing.c */ +#ifdef CONFIG_CRYPTO_FIPS140_MOD_EVAL_TESTING +void fips140_inject_selftest_failure(const char *impl, u8 *result); +void fips140_inject_integrity_failure(u8 *textcopy); +bool fips140_eval_testing_init(void); +#else +static inline void fips140_inject_selftest_failure(const char *impl, u8 *result) +{ +} +static inline void fips140_inject_integrity_failure(u8 *textcopy) +{ +} +static inline bool fips140_eval_testing_init(void) +{ + return true; +} +#endif /* !CONFIG_CRYPTO_FIPS140_MOD_EVAL_TESTING */ + +/* fips140-module.c */ +extern struct completion fips140_tests_done; +extern struct task_struct *fips140_init_thread; +bool fips140_is_approved_service(const char *name); +const char *fips140_module_version(void); + +/* fips140-selftests.c */ +bool __init __must_check fips140_run_selftests(void); + +#endif /* _CRYPTO_FIPS140_MODULE_H */ diff --git a/crypto/fips140-refs.S b/crypto/fips140-refs.S new file mode 100644 index 000000000000..fcbd52776323 --- /dev/null +++ b/crypto/fips140-refs.S @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2021 Google LLC + * Author: Ard Biesheuvel + * + * This file contains the variable definitions that will be used by the FIPS140 + * s/w module to access the RELA sections in the ELF image. These are used to + * apply the relocations applied by the module loader in reverse, so that we + * can reconstruct the image that was used to derive the HMAC used by the + * integrity check. + * + * The first .long of each entry will be populated by the module loader based + * on the actual placement of the respective RELA section in memory. The second + * .long carries the RELA entry count, and is populated by the host tool that + * also generates the HMAC of the contents of .text and .rodata. + */ + +#include +#include + + .section ".init.rodata", "a" + + .align 2 + .globl fips140_rela_text +fips140_rela_text: + .weak __sec_rela_text + .long __sec_rela_text - . + .long 0 + + .globl fips140_rela_rodata +fips140_rela_rodata: + .weak __sec_rela_rodata + .long __sec_rela_rodata - . + .long 0 diff --git a/crypto/fips140-selftests.c b/crypto/fips140-selftests.c new file mode 100644 index 000000000000..4f6a5d249b4c --- /dev/null +++ b/crypto/fips140-selftests.c @@ -0,0 +1,1020 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2021 Google LLC + * + * Authors: Elena Petrova , + * Eric Biggers + * + * Self-tests of fips140.ko cryptographic functionality. These are run at + * module load time to fulfill FIPS 140 and NIAP FPT_TST_EXT.1 requirements. + * + * The actual requirements for these self-tests are somewhat vague, but + * section 10 ("Self-Tests") of the FIPS 140-3 Implementation Guidance document + * (https://csrc.nist.gov/csrc/media/Projects/cryptographic-module-validation-program/documents/fips%20140-3/FIPS%20140-3%20IG.pdf) + * is somewhat helpful. Basically, all implementations of all FIPS approved + * algorithms (including modes of operation) must be tested. However: + * + * - There are provisions for skipping tests that are already sufficiently + * covered by other tests. E.g., HMAC-SHA256 may cover SHA-256. + * + * - Only one test vector is required per algorithm, and it can be generated + * by any known-good implementation or taken from any official document. + * + * - For ciphers, both encryption and decryption must be tested. + * + * - Only one key size per algorithm needs to be tested. + * + * There is some ambiguity about whether all implementations of each algorithm + * must be tested, or whether it is sufficient to test just the highest priority + * implementation. To be safe we test all implementations, except ones that can + * be excluded by one of the rules above. + * + * See fips140_selftests[] for the list of tests we've selected. Currently, all + * our test vectors except the AES-CBC-CTS and DRBG ones were generated by the + * script tools/crypto/gen_fips140_testvecs.py, using the known-good + * implementations in the Python packages hashlib, pycryptodome, and + * cryptography. + * + * Note that we don't reuse the upstream crypto API's self-tests + * (crypto/testmgr.{c,h}), for several reasons: + * + * - To meet FIPS requirements, the self-tests must be located within the FIPS + * module boundary (fips140.ko). But testmgr is integrated into the crypto + * API framework and can't be extracted into the module. + * + * - testmgr is much more heavyweight than required for FIPS and NIAP; it + * tests more algorithms and does more tests per algorithm, as it's meant to + * do proper testing and not just meet certification requirements. We need + * tests that can run with minimal overhead on every boot-up. + * + * - Despite being more heavyweight in general, testmgr doesn't test the + * SHA-256 and AES library APIs, despite that being needed here. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "fips140-module.h" + +/* Test vector for an AEAD algorithm */ +struct aead_testvec { + const u8 *key; + size_t key_size; + const u8 *iv; + size_t iv_size; + const u8 *assoc; + size_t assoc_size; + const u8 *plaintext; + size_t plaintext_size; + const u8 *ciphertext; + size_t ciphertext_size; +}; + +/* Test vector for a length-preserving encryption algorithm */ +struct skcipher_testvec { + const u8 *key; + size_t key_size; + const u8 *iv; + size_t iv_size; + const u8 *plaintext; + const u8 *ciphertext; + size_t message_size; +}; + +/* Test vector for a hash algorithm */ +struct hash_testvec { + const u8 *key; + size_t key_size; + const u8 *message; + size_t message_size; + const u8 *digest; + size_t digest_size; +}; + +/* Test vector for a DRBG algorithm */ +struct drbg_testvec { + const u8 *entropy; + size_t entropy_size; + const u8 *pers; + size_t pers_size; + const u8 *entpr_a; + const u8 *entpr_b; + size_t entpr_size; + const u8 *add_a; + const u8 *add_b; + size_t add_size; + const u8 *output; + size_t out_size; +}; + +struct fips_test { + /* The name of the algorithm, in crypto API syntax */ + const char *alg; + + /* + * The optional list of implementations to test. @func will be called + * once per implementation, or once with @alg if this list is empty. + * The implementation names must be given in crypto API syntax, or in + * the case of a library implementation should have "-lib" appended. + */ + const char *impls[8]; + + /* + * The test function. It should execute a known-answer test on an + * algorithm implementation, using the below test vector. + */ + int __must_check (*func)(const struct fips_test *test, + const char *impl); + + /* The test vector, with a format specific to the type of algorithm */ + union { + struct aead_testvec aead; + struct skcipher_testvec skcipher; + struct hash_testvec hash; + struct drbg_testvec drbg; + }; +}; + +/* Maximum IV size (in bytes) among any algorithm tested here */ +#define MAX_IV_SIZE 16 + +static int __init __must_check +fips_check_result(u8 *result, const u8 *expected_result, size_t result_size, + const char *impl, const char *operation) +{ + fips140_inject_selftest_failure(impl, result); + if (memcmp(result, expected_result, result_size) != 0) { + pr_err("wrong result from %s %s\n", impl, operation); + return -EBADMSG; + } + return 0; +} + +/* + * None of the algorithms should be ASYNC, as the FIPS module doesn't register + * any ASYNC algorithms. (The ASYNC flag is only declared by hardware + * algorithms, which would need their own FIPS certification.) + * + * Ideally we would verify alg->cra_module == THIS_MODULE here as well, but that + * doesn't work because the files are compiled as built-in code. + */ +static int __init __must_check +fips_validate_alg(const struct crypto_alg *alg) +{ + if (alg->cra_flags & CRYPTO_ALG_ASYNC) { + pr_err("unexpectedly got async implementation of %s (%s)\n", + alg->cra_name, alg->cra_driver_name); + return -EINVAL; + } + return 0; +} + +static int __init __must_check +fips_handle_alloc_tfm_error(const char *impl, int err) +{ + if (err == -ENOENT) { + /* + * The requested implementation of the algorithm wasn't found. + * This is expected if the CPU lacks a feature the + * implementation needs, such as the ARMv8 Crypto Extensions. + * + * When this happens, the implementation isn't available for + * use, so we can't test it, nor do we need to. So we just skip + * the test. + */ + pr_info("%s is unavailable (no CPU support?), skipping testing it\n", + impl); + return 0; + } + pr_err("failed to allocate %s tfm: %d\n", impl, err); + return err; +} + +static int __init __must_check +fips_test_aes_library(const struct fips_test *test, const char *impl) +{ + const struct skcipher_testvec *vec = &test->skcipher; + struct crypto_aes_ctx ctx; + u8 block[AES_BLOCK_SIZE]; + int err; + + if (WARN_ON(vec->message_size != AES_BLOCK_SIZE)) + return -EINVAL; + + err = aes_expandkey(&ctx, vec->key, vec->key_size); + if (err) { + pr_err("aes_expandkey() failed: %d\n", err); + return err; + } + aes_encrypt(&ctx, block, vec->plaintext); + err = fips_check_result(block, vec->ciphertext, AES_BLOCK_SIZE, + impl, "encryption"); + if (err) + return err; + aes_decrypt(&ctx, block, block); + return fips_check_result(block, vec->plaintext, AES_BLOCK_SIZE, + impl, "decryption"); +} + +/* Test a length-preserving symmetric cipher using the crypto_skcipher API. */ +static int __init __must_check +fips_test_skcipher(const struct fips_test *test, const char *impl) +{ + const struct skcipher_testvec *vec = &test->skcipher; + struct crypto_skcipher *tfm; + struct skcipher_request *req = NULL; + u8 *message = NULL; + struct scatterlist sg; + u8 iv[MAX_IV_SIZE]; + int err; + + if (WARN_ON(vec->iv_size > MAX_IV_SIZE)) + return -EINVAL; + if (WARN_ON(vec->message_size <= 0)) + return -EINVAL; + + tfm = crypto_alloc_skcipher(impl, 0, 0); + if (IS_ERR(tfm)) + return fips_handle_alloc_tfm_error(impl, PTR_ERR(tfm)); + err = fips_validate_alg(&crypto_skcipher_alg(tfm)->base); + if (err) + goto out; + if (crypto_skcipher_ivsize(tfm) != vec->iv_size) { + pr_err("%s has wrong IV size\n", impl); + err = -EINVAL; + goto out; + } + + req = skcipher_request_alloc(tfm, GFP_KERNEL); + message = kmemdup(vec->plaintext, vec->message_size, GFP_KERNEL); + if (!req || !message) { + err = -ENOMEM; + goto out; + } + sg_init_one(&sg, message, vec->message_size); + + skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, + NULL, NULL); + skcipher_request_set_crypt(req, &sg, &sg, vec->message_size, iv); + + err = crypto_skcipher_setkey(tfm, vec->key, vec->key_size); + if (err) { + pr_err("failed to set %s key: %d\n", impl, err); + goto out; + } + + /* Encrypt the plaintext, then verify the resulting ciphertext. */ + memcpy(iv, vec->iv, vec->iv_size); + err = crypto_skcipher_encrypt(req); + if (err) { + pr_err("%s encryption failed: %d\n", impl, err); + goto out; + } + err = fips_check_result(message, vec->ciphertext, vec->message_size, + impl, "encryption"); + if (err) + goto out; + + /* Decrypt the ciphertext, then verify the resulting plaintext. */ + memcpy(iv, vec->iv, vec->iv_size); + err = crypto_skcipher_decrypt(req); + if (err) { + pr_err("%s decryption failed: %d\n", impl, err); + goto out; + } + err = fips_check_result(message, vec->plaintext, vec->message_size, + impl, "decryption"); +out: + kfree(message); + skcipher_request_free(req); + crypto_free_skcipher(tfm); + return err; +} + +/* Test an AEAD using the crypto_aead API. */ +static int __init __must_check +fips_test_aead(const struct fips_test *test, const char *impl) +{ + const struct aead_testvec *vec = &test->aead; + const int tag_size = vec->ciphertext_size - vec->plaintext_size; + struct crypto_aead *tfm; + struct aead_request *req = NULL; + u8 *assoc = NULL; + u8 *message = NULL; + struct scatterlist sg[2]; + int sg_idx = 0; + u8 iv[MAX_IV_SIZE]; + int err; + + if (WARN_ON(vec->iv_size > MAX_IV_SIZE)) + return -EINVAL; + if (WARN_ON(vec->ciphertext_size <= vec->plaintext_size)) + return -EINVAL; + + tfm = crypto_alloc_aead(impl, 0, 0); + if (IS_ERR(tfm)) + return fips_handle_alloc_tfm_error(impl, PTR_ERR(tfm)); + err = fips_validate_alg(&crypto_aead_alg(tfm)->base); + if (err) + goto out; + if (crypto_aead_ivsize(tfm) != vec->iv_size) { + pr_err("%s has wrong IV size\n", impl); + err = -EINVAL; + goto out; + } + + req = aead_request_alloc(tfm, GFP_KERNEL); + assoc = kmemdup(vec->assoc, vec->assoc_size, GFP_KERNEL); + message = kzalloc(vec->ciphertext_size, GFP_KERNEL); + if (!req || !assoc || !message) { + err = -ENOMEM; + goto out; + } + memcpy(message, vec->plaintext, vec->plaintext_size); + + sg_init_table(sg, ARRAY_SIZE(sg)); + if (vec->assoc_size) + sg_set_buf(&sg[sg_idx++], assoc, vec->assoc_size); + sg_set_buf(&sg[sg_idx++], message, vec->ciphertext_size); + + aead_request_set_ad(req, vec->assoc_size); + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); + + err = crypto_aead_setkey(tfm, vec->key, vec->key_size); + if (err) { + pr_err("failed to set %s key: %d\n", impl, err); + goto out; + } + + err = crypto_aead_setauthsize(tfm, tag_size); + if (err) { + pr_err("failed to set %s authentication tag size: %d\n", + impl, err); + goto out; + } + + /* + * Encrypt the plaintext, then verify the resulting ciphertext (which + * includes the authentication tag). + */ + memcpy(iv, vec->iv, vec->iv_size); + aead_request_set_crypt(req, sg, sg, vec->plaintext_size, iv); + err = crypto_aead_encrypt(req); + if (err) { + pr_err("%s encryption failed: %d\n", impl, err); + goto out; + } + err = fips_check_result(message, vec->ciphertext, vec->ciphertext_size, + impl, "encryption"); + if (err) + goto out; + + /* + * Decrypt the ciphertext (which includes the authentication tag), then + * verify the resulting plaintext. + */ + memcpy(iv, vec->iv, vec->iv_size); + aead_request_set_crypt(req, sg, sg, vec->ciphertext_size, iv); + err = crypto_aead_decrypt(req); + if (err) { + pr_err("%s decryption failed: %d\n", impl, err); + goto out; + } + err = fips_check_result(message, vec->plaintext, vec->plaintext_size, + impl, "decryption"); +out: + kfree(message); + kfree(assoc); + aead_request_free(req); + crypto_free_aead(tfm); + return err; +} + +/* + * Test a hash algorithm using the crypto_shash API. + * + * Note that we don't need to test the crypto_ahash API too, since none of the + * hash algorithms in the FIPS module have the ASYNC flag, and thus there will + * be no hash algorithms that can be accessed only through crypto_ahash. + */ +static int __init __must_check +fips_test_hash(const struct fips_test *test, const char *impl) +{ + const struct hash_testvec *vec = &test->hash; + struct crypto_shash *tfm; + u8 digest[HASH_MAX_DIGESTSIZE]; + int err; + + if (WARN_ON(vec->digest_size > HASH_MAX_DIGESTSIZE)) + return -EINVAL; + + tfm = crypto_alloc_shash(impl, 0, 0); + if (IS_ERR(tfm)) + return fips_handle_alloc_tfm_error(impl, PTR_ERR(tfm)); + err = fips_validate_alg(&crypto_shash_alg(tfm)->base); + if (err) + goto out; + if (crypto_shash_digestsize(tfm) != vec->digest_size) { + pr_err("%s has wrong digest size\n", impl); + err = -EINVAL; + goto out; + } + + if (vec->key) { + err = crypto_shash_setkey(tfm, vec->key, vec->key_size); + if (err) { + pr_err("failed to set %s key: %d\n", impl, err); + goto out; + } + } + + err = crypto_shash_tfm_digest(tfm, vec->message, vec->message_size, + digest); + if (err) { + pr_err("%s digest computation failed: %d\n", impl, err); + goto out; + } + err = fips_check_result(digest, vec->digest, vec->digest_size, + impl, "digest"); +out: + crypto_free_shash(tfm); + return err; +} + +static int __init __must_check +fips_test_sha256_library(const struct fips_test *test, const char *impl) +{ + const struct hash_testvec *vec = &test->hash; + u8 digest[SHA256_DIGEST_SIZE]; + + if (WARN_ON(vec->digest_size != SHA256_DIGEST_SIZE)) + return -EINVAL; + + sha256(vec->message, vec->message_size, digest); + return fips_check_result(digest, vec->digest, vec->digest_size, + impl, "digest"); +} + +/* Test a DRBG using the crypto_rng API. */ +static int __init __must_check +fips_test_drbg(const struct fips_test *test, const char *impl) +{ + const struct drbg_testvec *vec = &test->drbg; + struct crypto_rng *rng; + u8 *output = NULL; + struct drbg_test_data test_data; + struct drbg_string addtl, pers, testentropy; + int err; + + rng = crypto_alloc_rng(impl, 0, 0); + if (IS_ERR(rng)) + return fips_handle_alloc_tfm_error(impl, PTR_ERR(rng)); + err = fips_validate_alg(&crypto_rng_alg(rng)->base); + if (err) + goto out; + + output = kzalloc(vec->out_size, GFP_KERNEL); + if (!output) { + err = -ENOMEM; + goto out; + } + + /* + * Initialize the DRBG with the entropy and personalization string given + * in the test vector. + */ + test_data.testentropy = &testentropy; + drbg_string_fill(&testentropy, vec->entropy, vec->entropy_size); + drbg_string_fill(&pers, vec->pers, vec->pers_size); + err = crypto_drbg_reset_test(rng, &pers, &test_data); + if (err) { + pr_err("failed to reset %s\n", impl); + goto out; + } + + /* + * Generate some random bytes using the additional data string provided + * in the test vector. Also use the additional entropy if provided + * (relevant for the prediction-resistant DRBG variants only). + */ + drbg_string_fill(&addtl, vec->add_a, vec->add_size); + if (vec->entpr_size) { + drbg_string_fill(&testentropy, vec->entpr_a, vec->entpr_size); + err = crypto_drbg_get_bytes_addtl_test(rng, output, + vec->out_size, &addtl, + &test_data); + } else { + err = crypto_drbg_get_bytes_addtl(rng, output, vec->out_size, + &addtl); + } + if (err) { + pr_err("failed to get bytes from %s (try 1): %d\n", + impl, err); + goto out; + } + + /* + * Do the same again, using a second additional data string, and (when + * applicable) a second additional entropy string. + */ + drbg_string_fill(&addtl, vec->add_b, vec->add_size); + if (test->drbg.entpr_size) { + drbg_string_fill(&testentropy, vec->entpr_b, vec->entpr_size); + err = crypto_drbg_get_bytes_addtl_test(rng, output, + vec->out_size, &addtl, + &test_data); + } else { + err = crypto_drbg_get_bytes_addtl(rng, output, vec->out_size, + &addtl); + } + if (err) { + pr_err("failed to get bytes from %s (try 2): %d\n", + impl, err); + goto out; + } + + /* Check that the DRBG generated the expected output. */ + err = fips_check_result(output, vec->output, vec->out_size, + impl, "get_bytes"); +out: + kfree(output); + crypto_free_rng(rng); + return err; +} + +/* Include the test vectors generated by the Python script. */ +#include "fips140-generated-testvecs.h" + +/* + * List of all self-tests. Keep this in sync with fips140_algs[]. + * + * When possible, we have followed the FIPS 140-3 Implementation Guidance (IG) + * document when creating this list of tests. The result is intended to be a + * list of tests that is near-minimal (and thus minimizes runtime overhead) + * while complying with all requirements. For additional details, see the + * comment at the beginning of this file. + */ +static const struct fips_test fips140_selftests[] __initconst = { + /* + * Test for the AES library API. + * + * Since the AES library API may use its own AES implementation and the + * module provides no support for composing it with a mode of operation + * (it's just plain AES), we must test it directly. + * + * In contrast, we don't need to directly test the "aes" ciphers that + * are accessible through the crypto_cipher API (e.g. "aes-ce"), as they + * are covered indirectly by AES-CMAC and AES-ECB tests. + */ + { + .alg = "aes", + .impls = {"aes-lib"}, + .func = fips_test_aes_library, + .skcipher = { + .key = fips_aes_key, + .key_size = sizeof(fips_aes_key), + .plaintext = fips_message, + .ciphertext = fips_aes_ecb_ciphertext, + .message_size = 16, + } + }, + /* + * Tests for AES-CMAC, a.k.a. "cmac(aes)" in crypto API syntax. + * + * The IG requires that each underlying AES implementation be tested in + * an authenticated mode, if implemented. Of such modes, this module + * implements AES-GCM and AES-CMAC. However, AES-GCM doesn't "count" + * because this module's implementations of AES-GCM won't actually be + * FIPS-approved, due to a quirk in the FIPS requirements. + * + * Therefore, for us this requirement applies to AES-CMAC, so we must + * test the "cmac" template composed with each "aes" implementation. + * + * Separately from the above, we also must test all standalone + * implementations of "cmac(aes)" such as "cmac-aes-ce", as they don't + * reuse another full AES implementation and thus can't be covered by + * another test. + */ + { + .alg = "cmac(aes)", + .impls = { + /* "cmac" template with all "aes" implementations */ + "cmac(aes-generic)", + "cmac(aes-arm64)", + "cmac(aes-ce)", + /* All standalone implementations of "cmac(aes)" */ + "cmac-aes-neon", + "cmac-aes-ce", + }, + .func = fips_test_hash, + .hash = { + .key = fips_aes_key, + .key_size = sizeof(fips_aes_key), + .message = fips_message, + .message_size = sizeof(fips_message), + .digest = fips_aes_cmac_digest, + .digest_size = sizeof(fips_aes_cmac_digest), + } + }, + /* + * Tests for AES-ECB, a.k.a. "ecb(aes)" in crypto API syntax. + * + * The IG requires that each underlying AES implementation be tested in + * a mode that exercises the encryption direction of AES and in a mode + * that exercises the decryption direction of AES. CMAC only covers the + * encryption direction, so we choose ECB to test decryption. Thus, we + * test the "ecb" template composed with each "aes" implementation. + * + * Separately from the above, we also must test all standalone + * implementations of "ecb(aes)" such as "ecb-aes-ce", as they don't + * reuse another full AES implementation and thus can't be covered by + * another test. + */ + { + .alg = "ecb(aes)", + .impls = { + /* "ecb" template with all "aes" implementations */ + "ecb(aes-generic)", + "ecb(aes-arm64)", + "ecb(aes-ce)", + /* All standalone implementations of "ecb(aes)" */ + "ecb-aes-neon", + "ecb-aes-neonbs", + "ecb-aes-ce", + }, + .func = fips_test_skcipher, + .skcipher = { + .key = fips_aes_key, + .key_size = sizeof(fips_aes_key), + .plaintext = fips_message, + .ciphertext = fips_aes_ecb_ciphertext, + .message_size = sizeof(fips_message) + } + }, + /* + * Tests for AES-CBC, AES-CBC-CTS, AES-CTR, AES-XTS, and AES-GCM. + * + * According to the IG, an AES mode of operation doesn't need to have + * its own test, provided that (a) both the encryption and decryption + * directions of the underlying AES implementation are already tested + * via other mode(s), and (b) in the case of an authenticated mode, at + * least one other authenticated mode is already tested. The tests of + * the "cmac" and "ecb" templates fulfill these conditions; therefore, + * we don't need to test any other AES mode templates. + * + * This does *not* apply to standalone implementations of these modes + * such as "cbc-aes-ce", as such implementations don't reuse another + * full AES implementation and thus can't be covered by another test. + * We must test all such standalone implementations. + * + * The AES-GCM test isn't actually required, as it's expected that this + * module's AES-GCM implementation won't actually be able to be + * FIPS-approved. This is unfortunate; it's caused by the FIPS + * requirements for GCM being incompatible with GCM implementations that + * don't generate their own IVs. We choose to still include the AES-GCM + * test to keep it on par with the other FIPS-approved algorithms, in + * case it turns out that AES-GCM can be approved after all. + */ + { + .alg = "cbc(aes)", + .impls = { + /* All standalone implementations of "cbc(aes)" */ + "cbc-aes-neon", + "cbc-aes-neonbs", + "cbc-aes-ce", + }, + .func = fips_test_skcipher, + .skcipher = { + .key = fips_aes_key, + .key_size = sizeof(fips_aes_key), + .iv = fips_aes_iv, + .iv_size = sizeof(fips_aes_iv), + .plaintext = fips_message, + .ciphertext = fips_aes_cbc_ciphertext, + .message_size = sizeof(fips_message), + } + }, { + .alg = "cts(cbc(aes))", + .impls = { + /* All standalone implementations of "cts(cbc(aes))" */ + "cts-cbc-aes-neon", + "cts-cbc-aes-ce", + }, + .func = fips_test_skcipher, + /* Test vector taken from RFC 3962 */ + .skcipher = { + .key = "\x63\x68\x69\x63\x6b\x65\x6e\x20" + "\x74\x65\x72\x69\x79\x61\x6b\x69", + .key_size = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .iv_size = 16, + .plaintext = "\x49\x20\x77\x6f\x75\x6c\x64\x20" + "\x6c\x69\x6b\x65\x20\x74\x68\x65" + "\x20\x47\x65\x6e\x65\x72\x61\x6c" + "\x20\x47\x61\x75\x27\x73\x20", + .ciphertext = "\xfc\x00\x78\x3e\x0e\xfd\xb2\xc1" + "\xd4\x45\xd4\xc8\xef\xf7\xed\x22" + "\x97\x68\x72\x68\xd6\xec\xcc\xc0" + "\xc0\x7b\x25\xe2\x5e\xcf\xe5", + .message_size = 31, + } + }, { + .alg = "ctr(aes)", + .impls = { + /* All standalone implementations of "ctr(aes)" */ + "ctr-aes-neon", + "ctr-aes-neonbs", + "ctr-aes-ce", + }, + .func = fips_test_skcipher, + .skcipher = { + .key = fips_aes_key, + .key_size = sizeof(fips_aes_key), + .iv = fips_aes_iv, + .iv_size = sizeof(fips_aes_iv), + .plaintext = fips_message, + .ciphertext = fips_aes_ctr_ciphertext, + .message_size = sizeof(fips_message), + } + }, { + .alg = "xts(aes)", + .impls = { + /* All standalone implementations of "xts(aes)" */ + "xts-aes-neon", + "xts-aes-neonbs", + "xts-aes-ce", + }, + .func = fips_test_skcipher, + .skcipher = { + .key = fips_aes_xts_key, + .key_size = sizeof(fips_aes_xts_key), + .iv = fips_aes_iv, + .iv_size = sizeof(fips_aes_iv), + .plaintext = fips_message, + .ciphertext = fips_aes_xts_ciphertext, + .message_size = sizeof(fips_message), + } + }, { + .alg = "gcm(aes)", + .impls = { + /* All standalone implementations of "gcm(aes)" */ + "gcm-aes-ce", + }, + .func = fips_test_aead, + .aead = { + .key = fips_aes_key, + .key_size = sizeof(fips_aes_key), + .iv = fips_aes_iv, + /* The GCM implementations assume an IV size of 12. */ + .iv_size = 12, + .assoc = fips_aes_gcm_assoc, + .assoc_size = sizeof(fips_aes_gcm_assoc), + .plaintext = fips_message, + .plaintext_size = sizeof(fips_message), + .ciphertext = fips_aes_gcm_ciphertext, + .ciphertext_size = sizeof(fips_aes_gcm_ciphertext), + } + }, + + /* Tests for SHA-1 */ + { + .alg = "sha1", + .impls = { + /* All implementations of "sha1" */ + "sha1-generic", + "sha1-ce" + }, + .func = fips_test_hash, + .hash = { + .message = fips_message, + .message_size = sizeof(fips_message), + .digest = fips_sha1_digest, + .digest_size = sizeof(fips_sha1_digest) + } + }, + /* + * Tests for all SHA-256 implementations other than the sha256() library + * function. As per the IG, these tests also fulfill the tests for the + * corresponding SHA-224 implementations. + */ + { + .alg = "sha256", + .impls = { + /* All implementations of "sha256" */ + "sha256-generic", + "sha256-arm64", + "sha256-ce", + }, + .func = fips_test_hash, + .hash = { + .message = fips_message, + .message_size = sizeof(fips_message), + .digest = fips_sha256_digest, + .digest_size = sizeof(fips_sha256_digest) + } + }, + /* + * Test for the sha256() library function. This must be tested + * separately because it may use its own SHA-256 implementation. + */ + { + .alg = "sha256", + .impls = {"sha256-lib"}, + .func = fips_test_sha256_library, + .hash = { + .message = fips_message, + .message_size = sizeof(fips_message), + .digest = fips_sha256_digest, + .digest_size = sizeof(fips_sha256_digest) + } + }, + /* + * Tests for all SHA-512 implementations. As per the IG, these tests + * also fulfill the tests for the corresponding SHA-384 implementations. + */ + { + .alg = "sha512", + .impls = { + /* All implementations of "sha512" */ + "sha512-generic", + "sha512-arm64", + "sha512-ce", + }, + .func = fips_test_hash, + .hash = { + .message = fips_message, + .message_size = sizeof(fips_message), + .digest = fips_sha512_digest, + .digest_size = sizeof(fips_sha512_digest) + } + }, + /* + * Test for HMAC. As per the IG, only one HMAC test is required, + * provided that the same HMAC code is shared by all HMAC-SHA*. This is + * true in our case. We choose HMAC-SHA256 for the test. + * + * Note that as per the IG, this can fulfill the test for the underlying + * SHA. However, we don't currently rely on this. + */ + { + .alg = "hmac(sha256)", + .func = fips_test_hash, + .hash = { + .key = fips_hmac_key, + .key_size = sizeof(fips_hmac_key), + .message = fips_message, + .message_size = sizeof(fips_message), + .digest = fips_hmac_sha256_digest, + .digest_size = sizeof(fips_hmac_sha256_digest) + } + }, + /* + * Test for SHA-3. As per the IG, only one SHA-3 variant needs to be + * tested. + */ + { + .alg = "sha3-256", + .impls = { + /* All implementations of "sha3-256" */ + "sha3-256-generic", + /* + * CONFIG_CRYPTO_SHA3_ARM64 is not currently enabled, + * since SHA-3 is only needed by jitterentropy. + */ + }, + .func = fips_test_hash, + .hash = { + .message = fips_message, + .message_size = sizeof(fips_message), + .digest = fips_sha3_256_digest, + .digest_size = sizeof(fips_sha3_256_digest) + } + }, + /* + * Known-answer tests for the SP800-90A DRBG algorithms. + * + * These test vectors were manually extracted from + * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/drbg/drbgtestvectors.zip. + * + * The selection of these tests follows the FIPS 140-3 IG as well as + * Section 11 of SP800-90A: + * + * - We must test all DRBG types (HMAC, Hash, and CTR) that the module + * implements. However, currently the module only implements + * HMAC_DRBG (since CONFIG_CRYPTO_DRBG_CTR and CONFIG_CRYPTO_DRBG_HASH + * aren't enabled). Therefore, we only need to test HMAC_DRBG. + * + * - We only need to test one HMAC variant. + * + * - We must test all DRBG operations: Instantiate(), Reseed(), and + * Generate(). However, a single test sequence with a single output + * comparison may cover all three operations, and this is what we do. + * Note that Reseed() happens implicitly via the use of the additional + * input and also via the use of prediction resistance when enabled. + * + * - The personalization string, additional input, and prediction + * resistance support must be tested. Therefore we have chosen test + * vectors that have a nonempty personalization string and nonempty + * additional input, and we test the prediction-resistant variant. + * Testing the non-prediction-resistant variant is not required. + */ + { + .alg = "drbg_pr_hmac_sha256", + .func = fips_test_drbg, + .drbg = { + .entropy = + "\xc7\xcc\xbc\x67\x7e\x21\x66\x1e\x27\x2b\x63\xdd" + "\x3a\x78\xdc\xdf\x66\x6d\x3f\x24\xae\xcf\x37\x01" + "\xa9\x0d\x89\x8a\xa7\xdc\x81\x58\xae\xb2\x10\x15" + "\x7e\x18\x44\x6d\x13\xea\xdf\x37\x85\xfe\x81\xfb", + .entropy_size = 48, + .entpr_a = + "\x7b\xa1\x91\x5b\x3c\x04\xc4\x1b\x1d\x19\x2f\x1a" + "\x18\x81\x60\x3c\x6c\x62\x91\xb7\xe9\xf5\xcb\x96" + "\xbb\x81\x6a\xcc\xb5\xae\x55\xb6", + .entpr_b = + "\x99\x2c\xc7\x78\x7e\x3b\x88\x12\xef\xbe\xd3\xd2" + "\x7d\x2a\xa5\x86\xda\x8d\x58\x73\x4a\x0a\xb2\x2e" + "\xbb\x4c\x7e\xe3\x9a\xb6\x81\xc1", + .entpr_size = 32, + .output = + "\x95\x6f\x95\xfc\x3b\xb7\xfe\x3e\xd0\x4e\x1a\x14" + "\x6c\x34\x7f\x7b\x1d\x0d\x63\x5e\x48\x9c\x69\xe6" + "\x46\x07\xd2\x87\xf3\x86\x52\x3d\x98\x27\x5e\xd7" + "\x54\xe7\x75\x50\x4f\xfb\x4d\xfd\xac\x2f\x4b\x77" + "\xcf\x9e\x8e\xcc\x16\xa2\x24\xcd\x53\xde\x3e\xc5" + "\x55\x5d\xd5\x26\x3f\x89\xdf\xca\x8b\x4e\x1e\xb6" + "\x88\x78\x63\x5c\xa2\x63\x98\x4e\x6f\x25\x59\xb1" + "\x5f\x2b\x23\xb0\x4b\xa5\x18\x5d\xc2\x15\x74\x40" + "\x59\x4c\xb4\x1e\xcf\x9a\x36\xfd\x43\xe2\x03\xb8" + "\x59\x91\x30\x89\x2a\xc8\x5a\x43\x23\x7c\x73\x72" + "\xda\x3f\xad\x2b\xba\x00\x6b\xd1", + .out_size = 128, + .add_a = + "\x18\xe8\x17\xff\xef\x39\xc7\x41\x5c\x73\x03\x03" + "\xf6\x3d\xe8\x5f\xc8\xab\xe4\xab\x0f\xad\xe8\xd6" + "\x86\x88\x55\x28\xc1\x69\xdd\x76", + .add_b = + "\xac\x07\xfc\xbe\x87\x0e\xd3\xea\x1f\x7e\xb8\xe7" + "\x9d\xec\xe8\xe7\xbc\xf3\x18\x25\x77\x35\x4a\xaa" + "\x00\x99\x2a\xdd\x0a\x00\x50\x82", + .add_size = 32, + .pers = + "\xbc\x55\xab\x3c\xf6\x52\xb0\x11\x3d\x7b\x90\xb8" + "\x24\xc9\x26\x4e\x5a\x1e\x77\x0d\x3d\x58\x4a\xda" + "\xd1\x81\xe9\xf8\xeb\x30\x8f\x6f", + .pers_size = 32, + } + } +}; + +static int __init __must_check +fips_run_test(const struct fips_test *test) +{ + int i; + int err; + + /* + * If no implementations were specified, then just test the default one. + * Otherwise, test the specified list of implementations. + */ + + if (test->impls[0] == NULL) { + err = test->func(test, test->alg); + if (err) + pr_emerg("self-tests failed for algorithm %s: %d\n", + test->alg, err); + return err; + } + + for (i = 0; i < ARRAY_SIZE(test->impls) && test->impls[i] != NULL; + i++) { + err = test->func(test, test->impls[i]); + if (err) { + pr_emerg("self-tests failed for algorithm %s, implementation %s: %d\n", + test->alg, test->impls[i], err); + return err; + } + } + return 0; +} + +bool __init fips140_run_selftests(void) +{ + int i; + + pr_info("running self-tests\n"); + for (i = 0; i < ARRAY_SIZE(fips140_selftests); i++) { + if (fips_run_test(&fips140_selftests[i]) != 0) { + /* The caller is responsible for calling panic(). */ + return false; + } + } + pr_info("all self-tests passed\n"); + return true; +} diff --git a/crypto/fips140_gen_hmac.c b/crypto/fips140_gen_hmac.c new file mode 100644 index 000000000000..69f754d38a1d --- /dev/null +++ b/crypto/fips140_gen_hmac.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 - Google LLC + * Author: Ard Biesheuvel + * + * This is a host tool that is intended to be used to take the HMAC digest of + * the .text and .rodata sections of the fips140.ko module, and store it inside + * the module. The module will perform an integrity selfcheck at module_init() + * time, by recalculating the digest and comparing it with the value calculated + * here. + * + * Note that the peculiar way an HMAC is being used as a digest with a public + * key rather than as a symmetric key signature is mandated by FIPS 140-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static Elf64_Ehdr *ehdr; +static Elf64_Shdr *shdr; +static int num_shdr; +static const char *strtab, *shstrtab; +static Elf64_Sym *syms; +static int num_syms; + +static Elf64_Shdr *find_symtab_section(void) +{ + int i; + + for (i = 0; i < num_shdr; i++) + if (shdr[i].sh_type == SHT_SYMTAB) + return &shdr[i]; + return NULL; +} + +static int get_section_idx(const char *name) +{ + int i; + + for (i = 0; i < num_shdr; i++) + if (!strcmp(shstrtab + shdr[i].sh_name, name)) + return i; + return -1; +} + +static int get_sym_idx(const char *sym_name) +{ + int i; + + for (i = 0; i < num_syms; i++) + if (!strcmp(strtab + syms[i].st_name, sym_name)) + return i; + return -1; +} + +static void *get_sym_addr(const char *sym_name) +{ + int i = get_sym_idx(sym_name); + + if (i >= 0) + return (void *)ehdr + shdr[syms[i].st_shndx].sh_offset + + syms[i].st_value; + return NULL; +} + +static int update_rela_ref(const char *name) +{ + /* + * We need to do a couple of things to ensure that the copied RELA data + * is accessible to the module itself at module init time: + * - the associated entry in the symbol table needs to refer to the + * correct section index, and have SECTION type and GLOBAL linkage. + * - the 'count' global variable in the module need to be set to the + * right value based on the size of the RELA section. + */ + unsigned int *size_var; + int sec_idx, sym_idx; + char str[32]; + + sprintf(str, "fips140_rela_%s", name); + size_var = get_sym_addr(str); + if (!size_var) { + printf("variable '%s' not found, disregarding .%s section\n", + str, name); + return 1; + } + + sprintf(str, "__sec_rela_%s", name); + sym_idx = get_sym_idx(str); + + sprintf(str, ".init.rela.%s", name); + sec_idx = get_section_idx(str); + + if (sec_idx < 0 || sym_idx < 0) { + fprintf(stderr, "failed to locate metadata for .%s section in binary\n", + name); + return 0; + } + + syms[sym_idx].st_shndx = sec_idx; + syms[sym_idx].st_info = (STB_GLOBAL << 4) | STT_SECTION; + + size_var[1] = shdr[sec_idx].sh_size / sizeof(Elf64_Rela); + + return 1; +} + +static void hmac_section(HMAC_CTX *hmac, const char *start, const char *end) +{ + void *start_addr = get_sym_addr(start); + void *end_addr = get_sym_addr(end); + + HMAC_Update(hmac, start_addr, end_addr - start_addr); +} + +int main(int argc, char **argv) +{ + Elf64_Shdr *symtab_shdr; + const char *hmac_key; + unsigned char *dg; + unsigned int dglen; + struct stat stat; + HMAC_CTX *hmac; + int fd, ret; + + if (argc < 2) { + fprintf(stderr, "file argument missing\n"); + exit(EXIT_FAILURE); + } + + fd = open(argv[1], O_RDWR); + if (fd < 0) { + fprintf(stderr, "failed to open %s\n", argv[1]); + exit(EXIT_FAILURE); + } + + ret = fstat(fd, &stat); + if (ret < 0) { + fprintf(stderr, "failed to stat() %s\n", argv[1]); + exit(EXIT_FAILURE); + } + + ehdr = mmap(0, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (ehdr == MAP_FAILED) { + fprintf(stderr, "failed to mmap() %s\n", argv[1]); + exit(EXIT_FAILURE); + } + + shdr = (void *)ehdr + ehdr->e_shoff; + num_shdr = ehdr->e_shnum; + + symtab_shdr = find_symtab_section(); + + syms = (void *)ehdr + symtab_shdr->sh_offset; + num_syms = symtab_shdr->sh_size / sizeof(Elf64_Sym); + + strtab = (void *)ehdr + shdr[symtab_shdr->sh_link].sh_offset; + shstrtab = (void *)ehdr + shdr[ehdr->e_shstrndx].sh_offset; + + if (!update_rela_ref("text") || !update_rela_ref("rodata")) + exit(EXIT_FAILURE); + + hmac_key = get_sym_addr("fips140_integ_hmac_key"); + if (!hmac_key) { + fprintf(stderr, "failed to locate HMAC key in binary\n"); + exit(EXIT_FAILURE); + } + + dg = get_sym_addr("fips140_integ_hmac_digest"); + if (!dg) { + fprintf(stderr, "failed to locate HMAC digest in binary\n"); + exit(EXIT_FAILURE); + } + + hmac = HMAC_CTX_new(); + HMAC_Init_ex(hmac, hmac_key, strlen(hmac_key), EVP_sha256(), NULL); + + hmac_section(hmac, "__fips140_text_start", "__fips140_text_end"); + hmac_section(hmac, "__fips140_rodata_start", "__fips140_rodata_end"); + + HMAC_Final(hmac, dg, &dglen); + + close(fd); + return 0; +} diff --git a/tools/crypto/gen_fips140_testvecs.py b/tools/crypto/gen_fips140_testvecs.py new file mode 100755 index 000000000000..2630fccc3b3b --- /dev/null +++ b/tools/crypto/gen_fips140_testvecs.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright 2021 Google LLC +# +# Generate most of the test vectors for the FIPS 140 cryptographic self-tests. +# +# Usage: +# tools/crypto/gen_fips140_testvecs.py > crypto/fips140-generated-testvecs.h +# +# Prerequisites: +# Debian: apt-get install python3-pycryptodome python3-cryptography +# Arch Linux: pacman -S python-pycryptodomex python-cryptography + +import hashlib +import hmac +import os + +import Cryptodome.Cipher.AES +import Cryptodome.Util.Counter + +import cryptography.hazmat.primitives.ciphers +import cryptography.hazmat.primitives.ciphers.algorithms +import cryptography.hazmat.primitives.ciphers.modes + +scriptname = os.path.basename(__file__) + +message = bytes('This is a 32-byte test message.\0', 'ascii') +aes_key = bytes('128-bit AES key\0', 'ascii') +aes_xts_key = bytes('This is an AES-128-XTS key.\0\0\0\0\0', 'ascii') +aes_iv = bytes('ABCDEFGHIJKLMNOP', 'ascii') +assoc = bytes('associated data string', 'ascii') +hmac_key = bytes('128-bit HMAC key', 'ascii') + +def warn_generated(): + print(f'''/* + * This header was automatically generated by {scriptname}. + * Don't edit it directly. + */''') + +def is_string_value(value): + return (value.isascii() and + all(c == '\x00' or c.isprintable() for c in str(value, 'ascii'))) + +def format_value(value, is_string): + if is_string: + return value + hexstr = '' + for byte in value: + hexstr += f'\\x{byte:02x}' + return hexstr + +def print_value(name, value): + is_string = is_string_value(value) + hdr = f'static const u8 fips_{name}[{len(value)}] __initconst =' + print(hdr, end='') + if is_string: + value = str(value, 'ascii').rstrip('\x00') + chars_per_byte = 1 + else: + chars_per_byte = 4 + bytes_per_line = 64 // chars_per_byte + + if len(hdr) + (chars_per_byte * len(value)) + 4 <= 80: + print(f' "{format_value(value, is_string)}"', end='') + else: + for chunk in [value[i:i+bytes_per_line] + for i in range(0, len(value), bytes_per_line)]: + print(f'\n\t"{format_value(chunk, is_string)}"', end='') + print(';') + print('') + +def generate_aes_testvecs(): + print_value('aes_key', aes_key) + print_value('aes_iv', aes_iv) + + cbc = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_CBC, + iv=aes_iv) + print_value('aes_cbc_ciphertext', cbc.encrypt(message)) + + ecb = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_ECB) + print_value('aes_ecb_ciphertext', ecb.encrypt(message)) + + ctr = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_CTR, + nonce=bytes(), initial_value=aes_iv) + print_value('aes_ctr_ciphertext', ctr.encrypt(message)) + + print_value('aes_gcm_assoc', assoc) + gcm = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_GCM, + nonce=aes_iv[:12], mac_len=16) + gcm.update(assoc) + raw_ciphertext, tag = gcm.encrypt_and_digest(message) + print_value('aes_gcm_ciphertext', raw_ciphertext + tag) + + # Unfortunately, pycryptodome doesn't support XTS, so for it we need to use + # a different Python package (the "cryptography" package). + print_value('aes_xts_key', aes_xts_key) + xts = cryptography.hazmat.primitives.ciphers.Cipher( + cryptography.hazmat.primitives.ciphers.algorithms.AES(aes_xts_key), + cryptography.hazmat.primitives.ciphers.modes.XTS(aes_iv)).encryptor() + ciphertext = xts.update(message) + xts.finalize() + print_value('aes_xts_ciphertext', ciphertext) + + cmac = Cryptodome.Hash.CMAC.new(aes_key, ciphermod=Cryptodome.Cipher.AES) + cmac.update(message) + print_value('aes_cmac_digest', cmac.digest()) + +def generate_sha_testvecs(): + print_value('hmac_key', hmac_key) + for alg in ['sha1', 'sha256', 'hmac_sha256', 'sha512', 'sha3_256']: + if alg.startswith('hmac_'): + h = hmac.new(hmac_key, message, alg.removeprefix('hmac_')) + else: + h = hashlib.new(alg, message) + print_value(f'{alg}_digest', h.digest()) + +print('/* SPDX-License-Identifier: GPL-2.0-only */') +print('/* Copyright 2021 Google LLC */') +print('') +warn_generated() +print('') +print_value('message', message) +generate_aes_testvecs() +generate_sha_testvecs() +warn_generated()