ANDROID: fips140: add kernel crypto module
To meet FIPS 140 requirements, add support for building a kernel module "fips140.ko" that contains various cryptographic algorithms built from existing kernel source files. At load time, the module checks its own integrity and self-tests its algorithms, then registers the algorithms with the crypto API to supersede the original algorithms provided by the kernel itself. [ebiggers: this commit originated from "ANDROID: crypto: fips140 - perform load time integrity check", but I've folded many later commits into it to make forward porting easier. See below] Original commits: android12-5.10: 6be141eb36fe ("ANDROID: crypto: fips140 - perform load time integrity check") 868be244bbed ("ANDROID: inject correct HMAC digest into fips140.ko at build time") 091338cb398e ("ANDROID: fips140: add missing static keyword to fips140_init()") c799c6644b52 ("ANDROID: fips140: adjust some log messages") 92de53472e68 ("ANDROID: fips140: log already-live algorithms") 0af06624eadc ("ANDROID: fips140: check for errors from initcalls") 634445a640a4 ("ANDROID: fips140: fix deadlock in unregister_existing_fips140_algos()") e886dd4c339e ("ANDROID: fips140: unregister existing DRBG algorithms") b7397e89db29 ("ANDROID: fips140: add power-up cryptographic self-tests") 50661975be74 ("ANDROID: fips140: add/update module help text") b397a0387cb2 ("ANDROID: fips140: test all implementations") 17ccefe14021 ("ANDROID: fips140: use full 16-byte IV") 1be58af0776a ("ANDROID: fips140: remove non-prediction-resistant DRBG test") 2b5843ae2d90 ("ANDROID: fips140: add AES-CBC-CTS") 2ee56aad318c ("ANDROID: fips140: add AES-CMAC") 960ebb2b565b ("ANDROID: fips140: add jitterentropy to fips140 module") e5b14396f9d2 ("ANDROID: fips140: take into account AES-GCM not being approvable") 52b70d491bd4 ("ANDROID: fips140: use FIPS140_CFLAGS when compiling fips140-selftests.c") 6b995f5a5403 ("ANDROID: fips140: preserve RELA sections without relying on the module loader") e45108ecff64 ("ANDROID: fips140: block crypto operations until tests complete") ecf9341134d1 ("ANDROID: fips140: remove in-place updating of live algorithms") 482b0323cf29 ("ANDROID: fips140: zeroize temporary values from integrity check") 64d769e53f20 ("ANDROID: fips140: add service indicators") 8d7f609cdaa4 ("ANDROID: fips140: add name and version, and a function to retrieve them") 6b7c37f6c449 ("ANDROID: fips140: use UTS_RELEASE as FIPS version") 903e97a0ca6d ("ANDROID: fips140: refactor evaluation testing support") 97fb2104fe22 ("ANDROID: fips140: add support for injecting integrity error") 109f31ac23f5 ("ANDROID: fips140: add userspace interface for evaluation testing") android14-5.15: 84572a0c7981 ("ANDROID: fips140: split dump-section+add-section into 2 ops") b0f8873811d4 ("ANDROID: kleaf: convert fips140 to kleaf") 2535deae8069 ("ANDROID: GKI: Source GKI_BUILD_CONFIG_FRAGMENT after setting all variables") 685a2ade28bb ("ANDROID: fips140: add crypto_memneq() back to the module") 320dfca58a3d ("ANDROID: fips140: fix in-tree builds") d4966a820397 ("ANDROID: fips140: remove CONFIG_CRYPTO_FIPS140 option") 6da26b8750f5 ("ANDROID: fips140: require 'm' to enable CRYPTO_FIPS140_MOD") bfcfcce3803b ("ANDROID: fips140: unapply ABS32 relocations generated by KCFI") 63f46b45dda2 ("ANDROID: fips140: eliminate crypto-fips.a build step") ae4ca7a09bb6 ("ANDROID: fips140: allow building without LTO") Bug: 153614920 Bug: 188620248 Test: tested that the module builds and can be loaded on raven. Change-Id: I3fde49dbc3d16b149b072a27ba5b4c6219015c94 Signed-off-by: Ard Biesheuvel <ardb@google.com> Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
parent
2bacdab575
commit
c6d5a76721
17
BUILD.bazel
17
BUILD.bazel
@ -317,6 +317,23 @@ copy_to_dist_dir(
|
||||
flat = True,
|
||||
)
|
||||
|
||||
kernel_build(
|
||||
name = "fips140",
|
||||
outs = [],
|
||||
base_kernel = ":kernel_aarch64",
|
||||
build_config = "build.config.gki.aarch64.fips140",
|
||||
module_outs = ["crypto/fips140.ko"],
|
||||
)
|
||||
|
||||
copy_to_dist_dir(
|
||||
name = "fips140_dist",
|
||||
data = [
|
||||
":fips140",
|
||||
],
|
||||
dist_dir = "out/fips140/dist",
|
||||
flat = True,
|
||||
)
|
||||
|
||||
# allmodconfig build tests.
|
||||
# These are build tests only, so:
|
||||
# - outs are intentionally set to empty to not copy anything to DIST_DIR
|
||||
|
49
arch/arm64/Makefile.postlink
Normal file
49
arch/arm64/Makefile.postlink
Normal file
@ -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 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)
|
2
arch/arm64/configs/fips140_gki.fragment
Normal file
2
arch/arm64/configs/fips140_gki.fragment
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_CRYPTO_FIPS140_MOD=m
|
||||
# CONFIG_MODULE_SIG_ALL is not set
|
52
arch/arm64/crypto/Kbuild.fips140
Normal file
52
arch/arm64/crypto/Kbuild.fips140
Normal file
@ -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)
|
27
build.config.gki.aarch64.fips140
Normal file
27
build.config.gki.aarch64.fips140
Normal file
@ -0,0 +1,27 @@
|
||||
. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common
|
||||
. ${ROOT_DIR}/${KERNEL_DIR}/build.config.aarch64
|
||||
. ${ROOT_DIR}/${KERNEL_DIR}/build.config.gki
|
||||
|
||||
FILES="
|
||||
crypto/fips140.ko
|
||||
"
|
||||
|
||||
MAKE_GOALS="
|
||||
modules
|
||||
"
|
||||
|
||||
if [ "${LTO}" = "none" ]; then
|
||||
echo "The FIPS140 module needs LTO to be enabled."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MODULES_ORDER=android/gki_aarch64_fips140_modules
|
||||
KERNEL_DIR=common
|
||||
|
||||
DEFCONFIG=fips140_gki_defconfig
|
||||
PRE_DEFCONFIG_CMDS="mkdir -p \${OUT_DIR}/arch/arm64/configs/ && KCONFIG_CONFIG=\${OUT_DIR}/arch/arm64/configs/${DEFCONFIG} ${ROOT_DIR}/${KERNEL_DIR}/scripts/kconfig/merge_config.sh -m -r ${ROOT_DIR}/${KERNEL_DIR}/arch/arm64/configs/gki_defconfig ${ROOT_DIR}/${KERNEL_DIR}/arch/arm64/configs/fips140_gki.fragment"
|
||||
POST_DEFCONFIG_CMDS=""
|
||||
|
||||
if [ -n "${GKI_BUILD_CONFIG_FRAGMENT}" ]; then
|
||||
source ${GKI_BUILD_CONFIG_FRAGMENT}
|
||||
fi
|
@ -54,6 +54,31 @@ 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 && ARM64_MODULE_PLTS
|
||||
depends on m
|
||||
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_ALGAPI
|
||||
tristate
|
||||
select CRYPTO_ALGAPI2
|
||||
|
@ -212,3 +212,56 @@ 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 \
|
||||
gf128mul.o aes_generic.o lib-crypto-aes.o \
|
||||
jitterentropy.o jitterentropy-kcapi.o \
|
||||
sha1_generic.o sha256_generic.o sha512_generic.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
|
||||
|
388
crypto/fips140-alg-registration.c
Normal file
388
crypto/fips140-alg-registration.c
Normal file
@ -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 <crypto/algapi.h>
|
||||
#include <crypto/internal/aead.h>
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <crypto/internal/rng.h>
|
||||
#include <crypto/internal/skcipher.h>
|
||||
#include <linux/xarray.h>
|
||||
|
||||
#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);
|
||||
}
|
58
crypto/fips140-defs.h
Normal file
58
crypto/fips140-defs.h
Normal file
@ -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 file that contains the real module_init() for fips140.ko is then
|
||||
* responsible for redefining MODULE, and the real module_init() is responsible
|
||||
* for executing all the 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
|
30
crypto/fips140-eval-testing-uapi.h
Normal file
30
crypto/fips140-eval-testing-uapi.h
Normal file
@ -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 <linux/ioctl.h>
|
||||
|
||||
/*
|
||||
* 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 */
|
129
crypto/fips140-eval-testing.c
Normal file
129
crypto/fips140-eval-testing.c
Normal file
@ -0,0 +1,129 @@
|
||||
// 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
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#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;
|
||||
}
|
68
crypto/fips140-generated-testvecs.h
Normal file
68
crypto/fips140-generated-testvecs.h
Normal file
@ -0,0 +1,68 @@
|
||||
/* 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";
|
||||
|
||||
/*
|
||||
* This header was automatically generated by gen_fips140_testvecs.py.
|
||||
* Don't edit it directly.
|
||||
*/
|
611
crypto/fips140-module.c
Normal file
611
crypto/fips140-module.c
Normal file
@ -0,0 +1,611 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright 2021 Google LLC
|
||||
* Author: Ard Biesheuvel <ardb@google.com>
|
||||
*
|
||||
* 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 <linux/ctype.h>
|
||||
#include <linux/module.h>
|
||||
#include <crypto/aead.h>
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/sha2.h>
|
||||
#include <crypto/skcipher.h>
|
||||
#include <crypto/rng.h>
|
||||
#include <trace/hooks/fips140.h>
|
||||
|
||||
#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 u32 __initcall_start_marker __section(".initcalls._start");
|
||||
const u32 __initcall_end_marker __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 __fips140_text_start and __fips140_rodata_start, which only exist
|
||||
* to delineate the section, and so their sizes are not relevant to us.
|
||||
*/
|
||||
const u32 *__initcall_start = &__initcall_start_marker;
|
||||
|
||||
const u8 *__text_start = &__fips140_text_start;
|
||||
const u8 *__rodata_start = &__fips140_rodata_start;
|
||||
|
||||
/*
|
||||
* The list of the crypto API algorithms (by cra_name) that will be unregistered
|
||||
* by this module, in preparation for the module registering its own
|
||||
* implementation(s) of them.
|
||||
*
|
||||
* All algorithms that will be declared as FIPS-approved in the module
|
||||
* certification must be listed here, to ensure that the non-FIPS-approved
|
||||
* implementations of these algorithms in the kernel image aren't used.
|
||||
*
|
||||
* For every algorithm in this list, the module should contain all the "same"
|
||||
* implementations that the kernel image does, including the C implementation as
|
||||
* well as any architecture-specific implementations. This is needed to avoid
|
||||
* performance regressions as well as the possibility of an algorithm being
|
||||
* unavailable on some CPUs. E.g., "xcbc(aes)" isn't in this list, as the
|
||||
* module doesn't have a C implementation of it (and it won't be FIPS-approved).
|
||||
*
|
||||
* Due to a quirk in the FIPS requirements, "gcm(aes)" isn't actually able to be
|
||||
* FIPS-approved. However, we otherwise treat it the same as the algorithms
|
||||
* that will be FIPS-approved, and therefore it's included in this list.
|
||||
*
|
||||
* When adding a new algorithm here, make sure to consider whether it needs a
|
||||
* self-test added to fips140_selftests[] as well.
|
||||
*/
|
||||
static const struct {
|
||||
const char *name;
|
||||
bool approved;
|
||||
} fips140_algs_to_replace[] = {
|
||||
{"aes", true},
|
||||
|
||||
{"cmac(aes)", true},
|
||||
{"ecb(aes)", true},
|
||||
|
||||
{"cbc(aes)", true},
|
||||
{"cts(cbc(aes))", true},
|
||||
{"ctr(aes)", true},
|
||||
{"xts(aes)", true},
|
||||
{"gcm(aes)", false},
|
||||
|
||||
{"hmac(sha1)", true},
|
||||
{"hmac(sha224)", true},
|
||||
{"hmac(sha256)", true},
|
||||
{"hmac(sha384)", true},
|
||||
{"hmac(sha512)", true},
|
||||
{"sha1", true},
|
||||
{"sha224", true},
|
||||
{"sha256", true},
|
||||
{"sha384", true},
|
||||
{"sha512", true},
|
||||
|
||||
{"stdrng", true},
|
||||
{"jitterentropy_rng", false},
|
||||
};
|
||||
|
||||
static bool __init fips140_should_unregister_alg(struct crypto_alg *alg)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* All software algorithms are synchronous, hardware algorithms must
|
||||
* be covered by their own FIPS 140 certification.
|
||||
*/
|
||||
if (alg->cra_flags & CRYPTO_ALG_ASYNC)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fips140_algs_to_replace); i++) {
|
||||
if (!strcmp(alg->cra_name, fips140_algs_to_replace[i].name))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_to_replace); i++) {
|
||||
if (!strcmp(name, fips140_algs_to_replace[i].name))
|
||||
return fips140_algs_to_replace[i].approved;
|
||||
}
|
||||
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 *alg, *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(alg, tmp, &crypto_alg_list, cra_list) {
|
||||
if (!fips140_should_unregister_alg(alg))
|
||||
continue;
|
||||
if (refcount_read(&alg->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.
|
||||
*/
|
||||
alg->cra_flags |= CRYPTO_ALG_DEAD;
|
||||
list_move(&alg->cra_list, &remove_list);
|
||||
crypto_remove_spawns(alg, &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 alg->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",
|
||||
alg->cra_name, alg->cra_driver_name);
|
||||
alg->cra_priority = 0;
|
||||
strlcat(alg->cra_name, "+orig", CRYPTO_MAX_ALG_NAME);
|
||||
strlcat(alg->cra_driver_name, "+orig",
|
||||
CRYPTO_MAX_ALG_NAME);
|
||||
list_move(&alg->cra_list, &existing_live_algos);
|
||||
}
|
||||
}
|
||||
up_write(&crypto_alg_sem);
|
||||
|
||||
fips140_remove_final(&remove_list);
|
||||
fips140_remove_final(&spawns);
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
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, __text_start, textsize);
|
||||
memcpy(rodatacopy, __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);
|
||||
|
||||
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.
|
||||
*
|
||||
* Note: this routine iterates over the contents of the initcall section, which
|
||||
* consists of an array of function pointers that was emitted by the linker
|
||||
* rather than the compiler. This means that these function pointers lack the
|
||||
* usual CFI stubs that the compiler emits when CFI codegen is enabled. So
|
||||
* let's disable CFI locally when handling the initcall array, to avoid
|
||||
* surpises.
|
||||
*/
|
||||
static int __init __attribute__((__no_sanitize__("cfi")))
|
||||
fips140_init(void)
|
||||
{
|
||||
const u32 *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 = __initcall_start + 1;
|
||||
initcall < &__initcall_end_marker;
|
||||
initcall++) {
|
||||
int (*init)(void) = 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;
|
||||
|
||||
/*
|
||||
* 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()) {
|
||||
pr_crit("integrity check failed -- giving up!\n");
|
||||
goto panic;
|
||||
}
|
||||
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);
|
||||
}
|
50
crypto/fips140-module.h
Normal file
50
crypto/fips140-module.h
Normal file
@ -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 <linux/completion.h>
|
||||
#include <linux/module.h>
|
||||
#include <generated/utsrelease.h>
|
||||
|
||||
#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 */
|
34
crypto/fips140-refs.S
Normal file
34
crypto/fips140-refs.S
Normal file
@ -0,0 +1,34 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright 2021 Google LLC
|
||||
* Author: Ard Biesheuvel <ardb@google.com>
|
||||
*
|
||||
* 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 <linux/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
|
||||
.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
|
998
crypto/fips140-selftests.c
Normal file
998
crypto/fips140-selftests.c
Normal file
@ -0,0 +1,998 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* Authors: Elena Petrova <lenaptr@google.com>,
|
||||
* Eric Biggers <ebiggers@google.com>
|
||||
*
|
||||
* 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 9 ("Self-Tests") of the FIPS 140-2 Implementation Guidance document
|
||||
* (https://csrc.nist.gov/csrc/media/projects/cryptographic-module-validation-program/documents/fips140-2/fips1402ig.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 <crypto/aead.h>
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/drbg.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/rng.h>
|
||||
#include <crypto/sha2.h>
|
||||
#include <crypto/skcipher.h>
|
||||
|
||||
#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_algorithms[].
|
||||
*
|
||||
* When possible, we have followed the FIPS 140-2 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)
|
||||
}
|
||||
},
|
||||
/*
|
||||
* 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-2 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;
|
||||
}
|
194
crypto/fips140_gen_hmac.c
Normal file
194
crypto/fips140_gen_hmac.c
Normal file
@ -0,0 +1,194 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2021 - Google LLC
|
||||
* Author: Ard Biesheuvel <ardb@google.com>
|
||||
*
|
||||
* 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 <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
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;
|
||||
}
|
125
tools/crypto/gen_fips140_testvecs.py
Executable file
125
tools/crypto/gen_fips140_testvecs.py
Executable file
@ -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']:
|
||||
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()
|
Loading…
Reference in New Issue
Block a user