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:
Ard Biesheuvel 2021-03-23 10:54:38 +01:00 committed by Eric Biggers
parent 2bacdab575
commit c6d5a76721
18 changed files with 2910 additions and 0 deletions

View File

@ -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

View 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)

View File

@ -0,0 +1,2 @@
CONFIG_CRYPTO_FIPS140_MOD=m
# CONFIG_MODULE_SIG_ALL is not set

View 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)

View 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

View File

@ -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

View File

@ -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

View 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
View 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

View 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 */

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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;
}

View 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()