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")
e8b59bcd783b ("ANDROID: fips140: fix the error injection module parameters")
9cef46f39ee9 ("ANDROID: remove LTO check from build.config.gki.aarch64.fips140")
b1f8c250264b ("ANDROID: fips140 - add option for debugging the integrity check")
7b301c70795e ("ANDROID: fips140 - fix integrity check by unapplying dynamic SCS")
android14-6.1:
3f5807c586a6 ("ANDROID: fips140: change linker script guard")
android15-6.6:
cb9ca9b4f2f4 ("ANDROID: fips140: avoid crypto driver name collisions")
8b1881ad162a ("ANDROID: fips140: add SHA-3 as an approved algorithm")
145f51aca09e ("ANDROID: fips140: remove unnecessary no_sanitize(cfi)")
eb1f7db04a47 ("ANDROID: fix kernelci GCC builds of fips140.ko")
Bug: 153614920
Bug: 188620248
Test: Tested that the module builds and can be loaded on a device.
Change-Id: I3fde49dbc3d16b149b072a27ba5b4c6219015c94
Signed-off-by: Ard Biesheuvel <ardb@google.com>
Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
committed by
Matthias Männich
parent
ce897685d1
commit
4084529fba
+25
@@ -1297,6 +1297,31 @@ pkg_install(
|
||||
destdir = "out/yukawa/dist",
|
||||
)
|
||||
|
||||
kernel_build(
|
||||
name = "fips140",
|
||||
outs = [],
|
||||
base_kernel = ":kernel_aarch64",
|
||||
build_config = "build.config.common",
|
||||
defconfig = "arch/arm64/configs/gki_defconfig",
|
||||
make_goals = ["modules"],
|
||||
makefile = ":Makefile",
|
||||
module_outs = ["crypto/fips140.ko"],
|
||||
post_defconfig_fragments = ["arch/arm64/configs/fips140_gki.fragment"],
|
||||
)
|
||||
|
||||
pkg_files(
|
||||
name = "fips140_dist_files",
|
||||
srcs = [":fips140"],
|
||||
strip_prefix = strip_prefix.files_only(),
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
pkg_install(
|
||||
name = "fips140_dist",
|
||||
srcs = ["fips140_dist_files"],
|
||||
destdir = "out/fips140/dist",
|
||||
)
|
||||
|
||||
# allmodconfig build tests.
|
||||
# These are build tests only, so:
|
||||
# - outs are intentionally set to empty to not copy anything to DIST_DIR
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#
|
||||
# This file is included by the generic Kbuild makefile to permit the
|
||||
# architecture to perform postlink actions on vmlinux and any .ko module file.
|
||||
# In this case, we only need it for fips140.ko, which needs some postprocessing
|
||||
# for the integrity check mandated by FIPS. This involves making copies of the
|
||||
# relocation sections so that the module will have access to them at
|
||||
# initialization time, and calculating and injecting a HMAC digest into the
|
||||
# module. All other targets are NOPs.
|
||||
#
|
||||
|
||||
PHONY := __archpost
|
||||
__archpost:
|
||||
|
||||
-include include/config/auto.conf
|
||||
include $(srctree)/scripts/Kbuild.include
|
||||
|
||||
CMD_FIPS140_GEN_HMAC = crypto/fips140_gen_hmac
|
||||
quiet_cmd_gen_hmac = HMAC $@
|
||||
cmd_gen_hmac = $(OBJCOPY) $@ \
|
||||
--dump-section=$(shell $(READELF) -SW $@|grep -Eo '\.rela\.text\S*')=$@.rela.text \
|
||||
--dump-section=$(shell $(READELF) -SW $@|grep -Eo '\.rela\.rodata\S*')=$@.rela.rodata && \
|
||||
$(OBJCOPY) $@ \
|
||||
--add-section=.init.rela.text=$@.rela.text \
|
||||
--add-section=.init.rela.rodata=$@.rela.rodata \
|
||||
--set-section-flags=.init.rela.text=alloc,readonly \
|
||||
--set-section-flags=.init.rela.rodata=alloc,readonly && \
|
||||
$(CMD_FIPS140_GEN_HMAC) $@
|
||||
|
||||
# `@true` prevents complaints when there is nothing to be done
|
||||
|
||||
vmlinux: FORCE
|
||||
@true
|
||||
|
||||
$(objtree)/crypto/fips140.ko: FORCE
|
||||
$(call cmd,gen_hmac)
|
||||
|
||||
%.ko: FORCE
|
||||
@true
|
||||
|
||||
clean:
|
||||
rm -f $(objtree)/crypto/fips140.ko.rela.*
|
||||
|
||||
PHONY += FORCE clean
|
||||
|
||||
FORCE:
|
||||
|
||||
.PHONY: $(PHONY)
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_CRYPTO_FIPS140_MOD=m
|
||||
# CONFIG_MODULE_SIG_ALL is not set
|
||||
@@ -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)
|
||||
@@ -54,6 +54,69 @@ config CRYPTO_FIPS_VERSION
|
||||
This option provides the ability to override the FIPS Module Version.
|
||||
By default the KERNELRELEASE value is used.
|
||||
|
||||
config CRYPTO_FIPS140_MOD
|
||||
tristate "Enable FIPS 140 cryptographic module"
|
||||
depends on ARM64
|
||||
depends on CC_IS_CLANG
|
||||
depends on m
|
||||
select CRYPTO_FIPS140_MERGE_MOD_SECTIONS
|
||||
help
|
||||
This option enables building a loadable module fips140.ko, which
|
||||
contains various crypto algorithms that are also built into vmlinux.
|
||||
At load time, this module overrides the built-in implementations of
|
||||
these algorithms with its implementations. It also runs self-tests on
|
||||
these algorithms and verifies the integrity of its code and data. If
|
||||
either of these steps fails, the kernel will panic.
|
||||
|
||||
This module is intended to be loaded at early boot time in order to
|
||||
meet FIPS 140 and NIAP FPT_TST_EXT.1 requirements. It shouldn't be
|
||||
used if you don't need to meet these requirements.
|
||||
|
||||
config CRYPTO_FIPS140_MOD_EVAL_TESTING
|
||||
bool "Enable evaluation testing features in FIPS 140 module"
|
||||
depends on CRYPTO_FIPS140_MOD
|
||||
help
|
||||
This option adds some features to the FIPS 140 module which are needed
|
||||
for lab evaluation testing of the module, e.g. support for injecting
|
||||
errors and support for a userspace interface to some of the module's
|
||||
services. This option should not be enabled in production builds.
|
||||
|
||||
config CRYPTO_FIPS140_MERGE_MOD_SECTIONS
|
||||
bool
|
||||
help
|
||||
This option causes the module linker script to place the delimeters
|
||||
of the text and rodata sections at the appropriate places so that
|
||||
the FIPS 140 integrity check can be performed. This option is required
|
||||
by the Crypto FIPS 140 module, and can be enabled by other FIPS 140
|
||||
modules using the same logic to perform the self integrity check.
|
||||
|
||||
config CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK
|
||||
bool "Debug the integrity check in FIPS 140 module"
|
||||
depends on CRYPTO_FIPS140_MOD
|
||||
help
|
||||
This option makes the FIPS 140 module provide debugfs files containing
|
||||
the text and rodata that were used for the integrity check, i.e. the
|
||||
runtime text and rodata with relocations and code patches unapplied.
|
||||
This option also makes the module load even if the integrity check
|
||||
fails so that these files can be used to debug the failure. (A
|
||||
possible failure mode is that the kernel has added a new type of code
|
||||
patching and the module needs to be updated to disable or unapply it.)
|
||||
|
||||
This option must not be enabled in production builds.
|
||||
|
||||
Example commands for debugging an integrity check failure:
|
||||
|
||||
adb root
|
||||
adb shell mount debugfs -t debugfs /sys/kernel/debug
|
||||
adb shell cp /sys/kernel/debug/fips140/{text,rodata} /data/local/tmp/
|
||||
adb pull /data/local/tmp/text text.checked
|
||||
adb pull /data/local/tmp/rodata rodata.checked
|
||||
llvm-objcopy -O binary --only-section=.text fips140.ko text.orig
|
||||
llvm-objcopy -O binary --only-section=.rodata fips140.ko rodata.orig
|
||||
for f in {text,rodata}.{orig,checked}; do xxd -g1 $f > $f.xxd; done
|
||||
vimdiff text.{orig,checked}.xxd
|
||||
vimdiff rodata.{orig,checked}.xxd
|
||||
|
||||
config CRYPTO_ALGAPI
|
||||
tristate
|
||||
select CRYPTO_ALGAPI2
|
||||
|
||||
@@ -208,3 +208,57 @@ obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o
|
||||
# Key derivation function
|
||||
#
|
||||
obj-$(CONFIG_CRYPTO_KDF800108_CTR) += kdf_sp800108.o
|
||||
|
||||
ifneq ($(CONFIG_CRYPTO_FIPS140_MOD),)
|
||||
|
||||
FIPS140_CFLAGS := -DBUILD_FIPS140_KO -include $(srctree)/crypto/fips140-defs.h
|
||||
|
||||
CFLAGS_jitterentropy-fips.o := -O0
|
||||
KASAN_SANITIZE_jitterentropy-fips.o = n
|
||||
UBSAN_SANITIZE_jitterentropy-fips.o = n
|
||||
|
||||
# Compile an extra copy of various crypto algorithms into the fips140 module.
|
||||
#
|
||||
# Note: the module will still work if some files are removed from here.
|
||||
# However, it may affect FIPS certifiability. Don't remove files from here
|
||||
# without considering impact on FIPS certifiability.
|
||||
|
||||
crypto-fips-objs := drbg.o ecb.o cbc.o ctr.o cts.o gcm.o xts.o hmac.o cmac.o \
|
||||
aes_generic.o \
|
||||
jitterentropy.o jitterentropy-kcapi.o \
|
||||
sha1_generic.o sha256_generic.o sha512_generic.o \
|
||||
sha3_generic.o \
|
||||
lib-crypto-aes.o lib-crypto-gf128mul.o lib-crypto-memneq.o \
|
||||
lib-crypto-sha1.o lib-crypto-sha256.o lib-crypto-utils.o
|
||||
crypto-fips-objs := $(foreach o,$(crypto-fips-objs),$(o:.o=-fips.o))
|
||||
|
||||
# get the arch to add its objects to $(crypto-fips-objs)
|
||||
include $(srctree)/arch/$(ARCH)/crypto/Kbuild.fips140
|
||||
|
||||
$(obj)/%-fips.o: KBUILD_CFLAGS += $(FIPS140_CFLAGS)
|
||||
$(obj)/%-fips.o: $(src)/%.c FORCE
|
||||
$(call if_changed_rule,cc_o_c)
|
||||
$(obj)/lib-%-fips.o: $(srctree)/lib/%.c FORCE
|
||||
$(call if_changed_rule,cc_o_c)
|
||||
$(obj)/lib-crypto-%-fips.o: $(srctree)/lib/crypto/%.c FORCE
|
||||
$(call if_changed_rule,cc_o_c)
|
||||
|
||||
fips140-objs := \
|
||||
fips140-alg-registration.o \
|
||||
fips140-module.o \
|
||||
fips140-refs.o \
|
||||
fips140-selftests.o \
|
||||
$(crypto-fips-objs)
|
||||
fips140-$(CONFIG_CRYPTO_FIPS140_MOD_EVAL_TESTING) += \
|
||||
fips140-eval-testing.o
|
||||
obj-m += fips140.o
|
||||
|
||||
CFLAGS_fips140-alg-registration.o += $(FIPS140_CFLAGS)
|
||||
CFLAGS_fips140-module.o += $(FIPS140_CFLAGS)
|
||||
CFLAGS_fips140-selftests.o += $(FIPS140_CFLAGS)
|
||||
CFLAGS_fips140-eval-testing.o += $(FIPS140_CFLAGS)
|
||||
|
||||
hostprogs-always-y := fips140_gen_hmac
|
||||
HOSTLDLIBS_fips140_gen_hmac := -lcrypto -lelf
|
||||
|
||||
endif
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* This file is automatically included by all files built into fips140.ko, via
|
||||
* the "-include" compiler flag.
|
||||
*/
|
||||
|
||||
/*
|
||||
* fips140.ko is built from various unmodified or minimally modified kernel
|
||||
* source files, many of which are normally meant to be buildable into different
|
||||
* modules themselves. That results in conflicting instances of module_init()
|
||||
* and related macros such as MODULE_LICENSE().
|
||||
*
|
||||
* To solve that, we undefine MODULE to trick the kernel headers into thinking
|
||||
* the code is being compiled as built-in. That causes module_init() and
|
||||
* related macros to be expanded as they would be for built-in code; e.g.,
|
||||
* module_init() adds the function to the .initcalls section of the binary.
|
||||
*
|
||||
* The .c files that contain the real module_init, module license, and module
|
||||
* parameters for fips140.ko are then responsible for redefining MODULE. The
|
||||
* real module_init executes all initcalls that were collected into .initcalls.
|
||||
*/
|
||||
#undef MODULE
|
||||
|
||||
/*
|
||||
* Defining KBUILD_MODFILE is also required, since the kernel headers expect it
|
||||
* to be defined when code that can be a module is compiled as built-in.
|
||||
*/
|
||||
#define KBUILD_MODFILE "crypto/fips140"
|
||||
|
||||
/*
|
||||
* Disable symbol exports by default. fips140.ko includes various files that
|
||||
* use EXPORT_SYMBOL*(), but it's unwanted to export any symbols from fips140.ko
|
||||
* except where explicitly needed for FIPS certification reasons.
|
||||
*/
|
||||
#define __DISABLE_EXPORTS
|
||||
|
||||
/*
|
||||
* Redirect all calls to algorithm registration functions to the wrapper
|
||||
* functions defined within the module.
|
||||
*/
|
||||
#define aead_register_instance fips140_aead_register_instance
|
||||
#define ahash_register_instance fips140_ahash_register_instance
|
||||
#define crypto_register_aead fips140_crypto_register_aead
|
||||
#define crypto_register_aeads fips140_crypto_register_aeads
|
||||
#define crypto_register_ahash fips140_crypto_register_ahash
|
||||
#define crypto_register_ahashes fips140_crypto_register_ahashes
|
||||
#define crypto_register_alg fips140_crypto_register_alg
|
||||
#define crypto_register_algs fips140_crypto_register_algs
|
||||
#define crypto_register_rng fips140_crypto_register_rng
|
||||
#define crypto_register_rngs fips140_crypto_register_rngs
|
||||
#define crypto_register_shash fips140_crypto_register_shash
|
||||
#define crypto_register_shashes fips140_crypto_register_shashes
|
||||
#define crypto_register_skcipher fips140_crypto_register_skcipher
|
||||
#define crypto_register_skciphers fips140_crypto_register_skciphers
|
||||
#define shash_register_instance fips140_shash_register_instance
|
||||
#define skcipher_register_instance fips140_skcipher_register_instance
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,137 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* This file can optionally be built into fips140.ko in order to support certain
|
||||
* types of testing that the FIPS lab has to do to evaluate the module. It
|
||||
* should not be included in production builds of the module.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We have to redefine inline to mean always_inline, so that _copy_to_user()
|
||||
* gets inlined. This is needed for it to be placed into the correct section.
|
||||
* See fips140_copy_to_user().
|
||||
*
|
||||
* We also need to undefine BUILD_FIPS140_KO to allow the use of the code
|
||||
* patching which copy_to_user() requires.
|
||||
*/
|
||||
#undef inline
|
||||
#define inline inline __attribute__((__always_inline__)) __gnu_inline \
|
||||
__inline_maybe_unused notrace
|
||||
#undef BUILD_FIPS140_KO
|
||||
|
||||
/*
|
||||
* Since this .c file contains real module parameters for fips140.ko, it needs
|
||||
* to be compiled normally, so undo the hacks that were done in fips140-defs.h.
|
||||
*/
|
||||
#define MODULE
|
||||
#undef KBUILD_MODFILE
|
||||
#undef __DISABLE_EXPORTS
|
||||
|
||||
#include <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;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright 2021 Google LLC */
|
||||
|
||||
/*
|
||||
* This header was automatically generated by gen_fips140_testvecs.py.
|
||||
* Don't edit it directly.
|
||||
*/
|
||||
|
||||
static const u8 fips_message[32] __initconst =
|
||||
"This is a 32-byte test message.";
|
||||
|
||||
static const u8 fips_aes_key[16] __initconst = "128-bit AES key";
|
||||
|
||||
static const u8 fips_aes_iv[16] __initconst = "ABCDEFGHIJKLMNOP";
|
||||
|
||||
static const u8 fips_aes_cbc_ciphertext[32] __initconst =
|
||||
"\x4c\x3e\xeb\x38\x8d\x1f\x28\xfd\xa2\x3b\xa9\xda\x36\xf2\x99\xe2"
|
||||
"\x84\x84\x66\x37\x0a\x53\x68\x2f\x17\x95\x8d\x7f\xca\x5a\x68\x4e";
|
||||
|
||||
static const u8 fips_aes_ecb_ciphertext[32] __initconst =
|
||||
"\xc1\x9d\xe6\xb8\xb2\x90\xff\xfe\xf2\x77\x18\xb0\x55\xd3\xee\xa9"
|
||||
"\xe2\x6f\x4a\x32\x67\xfd\xb7\xa5\x2f\x4b\x6e\x1a\x86\x2b\x6e\x3a";
|
||||
|
||||
static const u8 fips_aes_ctr_ciphertext[32] __initconst =
|
||||
"\xed\x06\x2c\xd0\xbc\x48\xd1\x2e\x6a\x4e\x13\xe9\xaa\x17\x40\xca"
|
||||
"\x00\xb4\xaf\x3b\x4f\xee\x73\xd6\x6c\x41\xf6\x4c\x8b\x0d\x6a\x0f";
|
||||
|
||||
static const u8 fips_aes_gcm_assoc[22] __initconst = "associated data string";
|
||||
|
||||
static const u8 fips_aes_gcm_ciphertext[48] __initconst =
|
||||
"\x37\x88\x3e\x1d\x58\x50\xda\x10\x07\xeb\x52\xdf\xea\x0a\x54\xd4"
|
||||
"\x44\xbf\x88\x2a\xf3\x03\x03\x84\xaf\x8b\x96\xbd\xea\x65\x60\x6f"
|
||||
"\x82\xfa\x51\xf4\x28\xad\x0c\xf1\xce\x0f\x91\xdd\x1a\x4c\x77\x5f";
|
||||
|
||||
static const u8 fips_aes_xts_key[32] __initconst =
|
||||
"This is an AES-128-XTS key.";
|
||||
|
||||
static const u8 fips_aes_xts_ciphertext[32] __initconst =
|
||||
"\x4f\xf7\x9f\x6c\x00\xa8\x30\xdf\xff\xf3\x25\x9c\xf6\x0b\x1b\xfd"
|
||||
"\x3b\x34\x5e\x67\x7c\xf8\x8b\x68\x9a\xb9\x5a\x89\x51\x51\xbd\x35";
|
||||
|
||||
static const u8 fips_aes_cmac_digest[16] __initconst =
|
||||
"\x0c\x05\xda\x64\x51\x0c\x8e\x6c\x86\x52\x46\xa8\x2d\xb1\xfe\x0f";
|
||||
|
||||
static const u8 fips_hmac_key[16] __initconst = "128-bit HMAC key";
|
||||
|
||||
static const u8 fips_sha1_digest[20] __initconst =
|
||||
"\x1b\x78\xc7\x4b\xd5\xd4\x83\xb1\x58\xc5\x96\x83\x4f\x16\x8d\x15"
|
||||
"\xb4\xaa\x22\x8c";
|
||||
|
||||
static const u8 fips_sha256_digest[32] __initconst =
|
||||
"\x4e\x11\x83\x0c\x53\x80\x1e\x5f\x9b\x38\x33\x38\xe8\x74\x43\xb0"
|
||||
"\xc1\x3a\xbe\xbf\x75\xf0\x12\x0f\x21\x33\xf5\x16\x33\xf1\xb0\x81";
|
||||
|
||||
static const u8 fips_hmac_sha256_digest[32] __initconst =
|
||||
"\x63\x0e\xb5\x73\x79\xfc\xaf\x5f\x86\xe3\xaf\xf0\xc8\x36\xef\xd5"
|
||||
"\x35\x8d\x40\x25\x38\xb3\x65\x72\x98\xf3\x59\xd8\x1e\x54\x4c\xa1";
|
||||
|
||||
static const u8 fips_sha512_digest[64] __initconst =
|
||||
"\x32\xe0\x44\x23\xbd\xe3\xec\x28\xbf\xf1\x34\x11\xd5\xae\xbf\xd5"
|
||||
"\xc0\x8e\xb5\xa1\x04\xef\x2f\x07\x84\xf1\xd9\x83\x0f\x6c\x31\xab"
|
||||
"\xf7\xe7\x57\xfa\xf7\xae\xf0\x6f\xb2\x16\x08\x32\xcf\xc7\xef\x35"
|
||||
"\xb3\x3b\x51\xb9\xfd\xe7\xff\x5e\xb2\x8b\xc6\x79\xe6\x14\x04\xb4";
|
||||
|
||||
static const u8 fips_sha3_256_digest[32] __initconst =
|
||||
"\xe5\x9f\x8a\x6d\x87\xbe\x4a\x76\x0a\xcd\xe3\x7e\x89\x28\xf6\xd3"
|
||||
"\xbc\x55\x0e\x5c\xf0\xec\x59\x33\xad\x9b\x9f\x09\x44\xa3\x88\xb8";
|
||||
|
||||
/*
|
||||
* This header was automatically generated by gen_fips140_testvecs.py.
|
||||
* Don't edit it directly.
|
||||
*/
|
||||
@@ -0,0 +1,817 @@
|
||||
// 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/debugfs.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 initcall_entry_t __fips140_initcalls_start __section(".initcalls._start");
|
||||
const initcall_entry_t __fips140_initcalls_end __section(".initcalls._end");
|
||||
|
||||
const u8 __fips140_text_start __section(".text.._start");
|
||||
const u8 __fips140_text_end __section(".text.._end");
|
||||
|
||||
const u8 __fips140_rodata_start __section(".rodata.._start");
|
||||
const u8 __fips140_rodata_end __section(".rodata.._end");
|
||||
|
||||
/*
|
||||
* We need this little detour to prevent Clang from detecting out of bounds
|
||||
* accesses to the above *_start symbols, which exist only to delineate the
|
||||
* corresponding sections, and so their sizes are not relevant to us.
|
||||
*/
|
||||
const initcall_entry_t *fips140_initcalls_start = &__fips140_initcalls_start;
|
||||
const u8 *fips140_text_start = &__fips140_text_start;
|
||||
const u8 *fips140_rodata_start = &__fips140_rodata_start;
|
||||
|
||||
/*
|
||||
* fips140_algs[] lists the algorithms that this module unregisters from the
|
||||
* kernel crypto API so that it can register its own implementation(s) of them.
|
||||
*
|
||||
* There are two reasons to do the unregistration, i.e. replace the kernel's
|
||||
* algorithms instead of just adding more algorithms. First, the kernel crypto
|
||||
* API doesn't allow algorithms with duplicate driver names. Second, for FIPS
|
||||
* approved algorithms we have to ensure that the FIPS copies are actually used.
|
||||
*/
|
||||
static struct fips140_alg {
|
||||
/*
|
||||
* Either cra_name or cra_driver_name is set.
|
||||
*
|
||||
* cra_name makes the entry match all software implementations of a
|
||||
* given algorithm. This is used when the module is meant to replace
|
||||
* *all* software implementations of the algorithm. This is required
|
||||
* for FIPS approved algorithms (approved=true). When using this style
|
||||
* of matching, it must be ensured that the module contains all the same
|
||||
* implementations of the algorithm as the kernel itself; otherwise the
|
||||
* kernel's functionality and/or performance could be impacted by the
|
||||
* insertion of the fips140 module.
|
||||
*
|
||||
* cra_driver_name makes the entry match a single implementation of an
|
||||
* algorithm. This is used for some specific non FIPS approved
|
||||
* algorithm implementations that get pulled in by being located in the
|
||||
* same source files as implementations of FIPS approved algorithms.
|
||||
*/
|
||||
const char *cra_name;
|
||||
const char *cra_driver_name;
|
||||
|
||||
/*
|
||||
* approved is true if fips140_is_approved_service() should return that
|
||||
* the algorithm is approved. This requires cra_name != NULL.
|
||||
*/
|
||||
bool approved;
|
||||
|
||||
/*
|
||||
* maybe_uninstantiated is true if the module provides this algorithm
|
||||
* but doesn't register it directly at module initialization time. This
|
||||
* occurs for some of the HMAC variants because they are provided by a
|
||||
* template which isn't immediately instantiated for every SHA variant
|
||||
* (since the HMAC self-test only has to test one SHA variant).
|
||||
*/
|
||||
bool maybe_uninstantiated;
|
||||
|
||||
/*
|
||||
* unregistered_inkern gets set to true at runtime if at least one
|
||||
* algorithm matching this entry was unregistered from the kernel. This
|
||||
* is used to detect unregistrations with no matching registration.
|
||||
*/
|
||||
bool unregistered_inkern;
|
||||
} fips140_algs[] = {
|
||||
/* Approved algorithms, all specified by cra_name */
|
||||
{ .cra_name = "aes", .approved = true },
|
||||
{ .cra_name = "cbc(aes)", .approved = true },
|
||||
{ .cra_name = "cmac(aes)", .approved = true },
|
||||
{ .cra_name = "ctr(aes)", .approved = true },
|
||||
{ .cra_name = "cts(cbc(aes))", .approved = true },
|
||||
{ .cra_name = "ecb(aes)", .approved = true },
|
||||
{ .cra_name = "hmac(sha1)", .approved = true,
|
||||
.maybe_uninstantiated = true },
|
||||
{ .cra_name = "hmac(sha224)", .approved = true,
|
||||
.maybe_uninstantiated = true },
|
||||
{ .cra_name = "hmac(sha256)", .approved = true },
|
||||
{ .cra_name = "hmac(sha384)", .approved = true,
|
||||
.maybe_uninstantiated = true },
|
||||
{ .cra_name = "hmac(sha512)", .approved = true,
|
||||
.maybe_uninstantiated = true },
|
||||
{ .cra_name = "sha1", .approved = true },
|
||||
{ .cra_name = "sha224", .approved = true },
|
||||
{ .cra_name = "sha256", .approved = true },
|
||||
{ .cra_name = "sha384", .approved = true },
|
||||
{ .cra_name = "sha512", .approved = true },
|
||||
{ .cra_name = "sha3-224", .approved = true },
|
||||
{ .cra_name = "sha3-256", .approved = true },
|
||||
{ .cra_name = "sha3-384", .approved = true },
|
||||
{ .cra_name = "sha3-512", .approved = true },
|
||||
{ .cra_name = "stdrng", .approved = true },
|
||||
{ .cra_name = "xts(aes)", .approved = true },
|
||||
|
||||
/*
|
||||
* Non-approved algorithms specified by cra_name.
|
||||
*
|
||||
* Due to a quirk in the FIPS requirements, AES-GCM can't be FIPS
|
||||
* approved. But we treat it the same as approved algorithms in that we
|
||||
* ensure that a self-test and all needed implementations are included.
|
||||
*
|
||||
* The Jitter RNG is needed in the module as an entropy source for the
|
||||
* DRBG algorithms, but it's not considered to be approved itself.
|
||||
*/
|
||||
{ .cra_name = "gcm(aes)" },
|
||||
{ .cra_name = "jitterentropy_rng" },
|
||||
|
||||
/* Non-approved algorithms specified by cra_driver_name */
|
||||
{ .cra_driver_name = "essiv-cbc-aes-sha256-ce" },
|
||||
{ .cra_driver_name = "essiv-cbc-aes-sha256-neon" },
|
||||
{ .cra_driver_name = "cbcmac-aes-ce" },
|
||||
{ .cra_driver_name = "cbcmac-aes-neon" },
|
||||
{ .cra_driver_name = "rfc4106-gcm-aes-ce" },
|
||||
{ .cra_driver_name = "xcbc-aes-ce" },
|
||||
{ .cra_driver_name = "xcbc-aes-neon" },
|
||||
{ .cra_driver_name = "xctr-aes-ce" },
|
||||
{ .cra_driver_name = "xctr-aes-neon" },
|
||||
};
|
||||
|
||||
/*
|
||||
* Return true if the crypto API algorithm @calg is matched by the fips140
|
||||
* module algorithm specification @falg.
|
||||
*/
|
||||
static bool __init fips140_alg_matches(const struct fips140_alg *falg,
|
||||
const struct crypto_alg *calg)
|
||||
{
|
||||
/*
|
||||
* All software algorithms are synchronous. Hardware algorithms must be
|
||||
* covered by their own FIPS 140 certification.
|
||||
*/
|
||||
if (calg->cra_flags & CRYPTO_ALG_ASYNC)
|
||||
return false;
|
||||
|
||||
if (falg->cra_name != NULL &&
|
||||
strcmp(falg->cra_name, calg->cra_name) == 0)
|
||||
return true;
|
||||
|
||||
if (falg->cra_driver_name != NULL &&
|
||||
strcmp(falg->cra_driver_name, calg->cra_driver_name) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find the entry in fips140_algs[], if any, that @calg is matched by. */
|
||||
static struct fips140_alg *__init
|
||||
fips140_find_matching_alg(const struct crypto_alg *calg)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fips140_algs); i++) {
|
||||
if (fips140_alg_matches(&fips140_algs[i], calg))
|
||||
return &fips140_algs[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIPS 140-3 service indicators. FIPS 140-3 requires that all services
|
||||
* "provide an indicator when the service utilises an approved cryptographic
|
||||
* algorithm, security function or process in an approved manner". What this
|
||||
* means is very debatable, even with the help of the FIPS 140-3 Implementation
|
||||
* Guidance document. However, it was decided that a function that takes in an
|
||||
* algorithm name and returns whether that algorithm is approved or not will
|
||||
* meet this requirement. Note, this relies on some properties of the module:
|
||||
*
|
||||
* - The module doesn't distinguish between "services" and "algorithms"; its
|
||||
* services are simply its algorithms.
|
||||
*
|
||||
* - The status of an approved algorithm is never non-approved, since (a) the
|
||||
* module doesn't support operating in a non-approved mode, such as a mode
|
||||
* where the self-tests are skipped; (b) there are no cases where the module
|
||||
* supports non-approved settings for approved algorithms, e.g.
|
||||
* non-approved key sizes; and (c) this function isn't available to be
|
||||
* called until the module_init function has completed, so it's guaranteed
|
||||
* that the self-tests and integrity check have already passed.
|
||||
*
|
||||
* - The module does support some non-approved algorithms, so a single static
|
||||
* indicator ("return true;") would not be acceptable.
|
||||
*/
|
||||
bool fips140_is_approved_service(const char *name)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fips140_algs); i++) {
|
||||
if (fips140_algs[i].approved &&
|
||||
fips140_algs[i].cra_name != NULL &&
|
||||
strcmp(name, fips140_algs[i].cra_name) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fips140_is_approved_service);
|
||||
|
||||
/*
|
||||
* FIPS 140-3 requires that modules provide a "service" that outputs "the name
|
||||
* or module identifier and the versioning information that can be correlated
|
||||
* with a validation record". This function meets that requirement.
|
||||
*
|
||||
* Note: the module also prints this same information to the kernel log when it
|
||||
* is loaded. That might meet the requirement by itself. However, given the
|
||||
* vagueness of what counts as a "service", we provide this function too, just
|
||||
* in case the certification lab or CMVP is happier with an explicit function.
|
||||
*
|
||||
* Note: /sys/modules/fips140/scmversion also provides versioning information
|
||||
* about the module. However that file just shows the bare git commit ID, so it
|
||||
* probably isn't sufficient to meet the FIPS requirement, which seems to want
|
||||
* the "official" module name and version number used in the FIPS certificate.
|
||||
*/
|
||||
const char *fips140_module_version(void)
|
||||
{
|
||||
return FIPS140_MODULE_NAME " " FIPS140_MODULE_VERSION;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fips140_module_version);
|
||||
|
||||
static LIST_HEAD(existing_live_algos);
|
||||
|
||||
/*
|
||||
* Release a list of algorithms which have been removed from crypto_alg_list.
|
||||
*
|
||||
* Note that even though the list is a private list, we have to hold
|
||||
* crypto_alg_sem while iterating through it because crypto_unregister_alg() may
|
||||
* run concurrently (as we haven't taken a reference to the algorithms on the
|
||||
* list), and crypto_unregister_alg() will remove the algorithm from whichever
|
||||
* list it happens to be on, while holding crypto_alg_sem. That's okay, since
|
||||
* in that case crypto_unregister_alg() will handle the crypto_alg_put().
|
||||
*/
|
||||
static void fips140_remove_final(struct list_head *list)
|
||||
{
|
||||
struct crypto_alg *alg;
|
||||
struct crypto_alg *n;
|
||||
|
||||
/*
|
||||
* We need to take crypto_alg_sem to safely traverse the list (see
|
||||
* comment above), but we have to drop it when doing each
|
||||
* crypto_alg_put() as that may take crypto_alg_sem again.
|
||||
*/
|
||||
down_write(&crypto_alg_sem);
|
||||
list_for_each_entry_safe(alg, n, list, cra_list) {
|
||||
list_del_init(&alg->cra_list);
|
||||
up_write(&crypto_alg_sem);
|
||||
|
||||
crypto_alg_put(alg);
|
||||
|
||||
down_write(&crypto_alg_sem);
|
||||
}
|
||||
up_write(&crypto_alg_sem);
|
||||
}
|
||||
|
||||
static void __init unregister_existing_fips140_algos(void)
|
||||
{
|
||||
struct crypto_alg *calg, *tmp;
|
||||
LIST_HEAD(remove_list);
|
||||
LIST_HEAD(spawns);
|
||||
|
||||
down_write(&crypto_alg_sem);
|
||||
|
||||
/*
|
||||
* Find all registered algorithms that we care about, and move them to a
|
||||
* private list so that they are no longer exposed via the algo lookup
|
||||
* API. Subsequently, we will unregister them if they are not in active
|
||||
* use. If they are, we can't fully unregister them but we can ensure
|
||||
* that new users won't use them.
|
||||
*/
|
||||
list_for_each_entry_safe(calg, tmp, &crypto_alg_list, cra_list) {
|
||||
struct fips140_alg *falg = fips140_find_matching_alg(calg);
|
||||
|
||||
if (!falg)
|
||||
continue;
|
||||
falg->unregistered_inkern = true;
|
||||
|
||||
if (refcount_read(&calg->cra_refcnt) == 1) {
|
||||
/*
|
||||
* This algorithm is not currently in use, but there may
|
||||
* be template instances holding references to it via
|
||||
* spawns. So let's tear it down like
|
||||
* crypto_unregister_alg() would, but without releasing
|
||||
* the lock, to prevent races with concurrent TFM
|
||||
* allocations.
|
||||
*/
|
||||
calg->cra_flags |= CRYPTO_ALG_DEAD;
|
||||
list_move(&calg->cra_list, &remove_list);
|
||||
crypto_remove_spawns(calg, &spawns, NULL);
|
||||
} else {
|
||||
/*
|
||||
* This algorithm is live, i.e. it has TFMs allocated,
|
||||
* so we can't fully unregister it. It's not necessary
|
||||
* to dynamically redirect existing users to the FIPS
|
||||
* code, given that they can't be relying on FIPS
|
||||
* certified crypto in the first place. However, we do
|
||||
* need to ensure that new users will get the FIPS code.
|
||||
*
|
||||
* In most cases, setting calg->cra_priority to 0
|
||||
* achieves this. However, that isn't enough for
|
||||
* algorithms like "hmac(sha256)" that need to be
|
||||
* instantiated from a template, since existing
|
||||
* algorithms always take priority over a template being
|
||||
* instantiated. Therefore, we move the algorithm to
|
||||
* a private list so that algorithm lookups won't find
|
||||
* it anymore. To further distinguish it from the FIPS
|
||||
* algorithms, we also append "+orig" to its name.
|
||||
*/
|
||||
pr_info("found already-live algorithm '%s' ('%s')\n",
|
||||
calg->cra_name, calg->cra_driver_name);
|
||||
calg->cra_priority = 0;
|
||||
strlcat(calg->cra_name, "+orig", CRYPTO_MAX_ALG_NAME);
|
||||
strlcat(calg->cra_driver_name, "+orig",
|
||||
CRYPTO_MAX_ALG_NAME);
|
||||
list_move(&calg->cra_list, &existing_live_algos);
|
||||
}
|
||||
}
|
||||
up_write(&crypto_alg_sem);
|
||||
|
||||
fips140_remove_final(&remove_list);
|
||||
fips140_remove_final(&spawns);
|
||||
}
|
||||
|
||||
/*
|
||||
* The algorithms unregistered by fips140.ko are determined by fips140_algs[],
|
||||
* but the algorithms registered by fips140.ko are determined by its initcalls.
|
||||
* There is a chance these get out of sync. Therefore, this function detects
|
||||
* cases where an algorithm was unregistered without a replacement being
|
||||
* registered. It returns true if things look ok or false if there's a problem.
|
||||
*/
|
||||
static bool __init fips140_verify_no_extra_unregistrations(void)
|
||||
{
|
||||
bool ok = true;
|
||||
size_t i;
|
||||
|
||||
down_read(&crypto_alg_sem);
|
||||
for (i = 0; i < ARRAY_SIZE(fips140_algs); i++) {
|
||||
const struct fips140_alg *falg = &fips140_algs[i];
|
||||
const struct crypto_alg *calg;
|
||||
bool registered = false;
|
||||
|
||||
if (falg->maybe_uninstantiated || !falg->unregistered_inkern)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(calg, &crypto_alg_list, cra_list) {
|
||||
if (fips140_alg_matches(falg, calg)) {
|
||||
registered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!registered) {
|
||||
pr_err("This module unregistered %s but did not replace it!\n",
|
||||
falg->cra_name ?: falg->cra_driver_name);
|
||||
pr_err("Either remove it from fips140_algs[], or fix the module to include it.\n");
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
up_read(&crypto_alg_sem);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void __init unapply_text_relocations(void *section, int section_size,
|
||||
const Elf64_Rela *rela, int numrels)
|
||||
{
|
||||
while (numrels--) {
|
||||
u32 *place = (u32 *)(section + rela->r_offset);
|
||||
|
||||
BUG_ON(rela->r_offset >= section_size);
|
||||
|
||||
switch (ELF64_R_TYPE(rela->r_info)) {
|
||||
#ifdef CONFIG_ARM64
|
||||
case R_AARCH64_ABS32: /* for KCFI */
|
||||
*place = 0;
|
||||
break;
|
||||
|
||||
case R_AARCH64_JUMP26:
|
||||
case R_AARCH64_CALL26:
|
||||
*place &= ~GENMASK(25, 0);
|
||||
break;
|
||||
|
||||
case R_AARCH64_ADR_PREL_LO21:
|
||||
case R_AARCH64_ADR_PREL_PG_HI21:
|
||||
case R_AARCH64_ADR_PREL_PG_HI21_NC:
|
||||
*place &= ~(GENMASK(30, 29) | GENMASK(23, 5));
|
||||
break;
|
||||
|
||||
case R_AARCH64_ADD_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST8_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST16_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST32_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST64_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST128_ABS_LO12_NC:
|
||||
*place &= ~GENMASK(21, 10);
|
||||
break;
|
||||
default:
|
||||
pr_err("unhandled relocation type %llu\n",
|
||||
ELF64_R_TYPE(rela->r_info));
|
||||
BUG();
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
rela++;
|
||||
}
|
||||
}
|
||||
|
||||
static void __init unapply_rodata_relocations(void *section, int section_size,
|
||||
const Elf64_Rela *rela, int numrels)
|
||||
{
|
||||
while (numrels--) {
|
||||
void *place = section + rela->r_offset;
|
||||
|
||||
BUG_ON(rela->r_offset >= section_size);
|
||||
|
||||
switch (ELF64_R_TYPE(rela->r_info)) {
|
||||
#ifdef CONFIG_ARM64
|
||||
case R_AARCH64_ABS64:
|
||||
*(u64 *)place = 0;
|
||||
break;
|
||||
default:
|
||||
pr_err("unhandled relocation type %llu\n",
|
||||
ELF64_R_TYPE(rela->r_info));
|
||||
BUG();
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
rela++;
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
PACIASP = 0xd503233f,
|
||||
AUTIASP = 0xd50323bf,
|
||||
SCS_PUSH = 0xf800865e,
|
||||
SCS_POP = 0xf85f8e5e,
|
||||
};
|
||||
|
||||
/*
|
||||
* To make the integrity check work with dynamic Shadow Call Stack (SCS),
|
||||
* replace all instructions that push or pop from the SCS with the Pointer
|
||||
* Authentication Code (PAC) instructions that were present originally.
|
||||
*/
|
||||
static void __init unapply_scs_patch(void *section, int section_size)
|
||||
{
|
||||
#if defined(CONFIG_ARM64) && defined(CONFIG_UNWIND_PATCH_PAC_INTO_SCS)
|
||||
u32 *insns = section;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < section_size / sizeof(insns[0]); i++) {
|
||||
if (insns[i] == SCS_PUSH)
|
||||
insns[i] = PACIASP;
|
||||
else if (insns[i] == SCS_POP)
|
||||
insns[i] = AUTIASP;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK
|
||||
static struct {
|
||||
const void *text;
|
||||
int textsize;
|
||||
const void *rodata;
|
||||
int rodatasize;
|
||||
} saved_integrity_check_info;
|
||||
|
||||
static ssize_t fips140_text_read(struct file *file, char __user *to,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return simple_read_from_buffer(to, count, ppos,
|
||||
saved_integrity_check_info.text,
|
||||
saved_integrity_check_info.textsize);
|
||||
}
|
||||
|
||||
static ssize_t fips140_rodata_read(struct file *file, char __user *to,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return simple_read_from_buffer(to, count, ppos,
|
||||
saved_integrity_check_info.rodata,
|
||||
saved_integrity_check_info.rodatasize);
|
||||
}
|
||||
|
||||
static const struct file_operations fips140_text_fops = {
|
||||
.read = fips140_text_read,
|
||||
};
|
||||
|
||||
static const struct file_operations fips140_rodata_fops = {
|
||||
.read = fips140_rodata_read,
|
||||
};
|
||||
|
||||
static void fips140_init_integrity_debug_files(const void *text, int textsize,
|
||||
const void *rodata,
|
||||
int rodatasize)
|
||||
{
|
||||
struct dentry *dir;
|
||||
|
||||
dir = debugfs_create_dir("fips140", NULL);
|
||||
|
||||
saved_integrity_check_info.text = kmemdup(text, textsize, GFP_KERNEL);
|
||||
saved_integrity_check_info.textsize = textsize;
|
||||
if (saved_integrity_check_info.text)
|
||||
debugfs_create_file("text", 0400, dir, NULL,
|
||||
&fips140_text_fops);
|
||||
|
||||
saved_integrity_check_info.rodata = kmemdup(rodata, rodatasize,
|
||||
GFP_KERNEL);
|
||||
saved_integrity_check_info.rodatasize = rodatasize;
|
||||
if (saved_integrity_check_info.rodata)
|
||||
debugfs_create_file("rodata", 0400, dir, NULL,
|
||||
&fips140_rodata_fops);
|
||||
}
|
||||
#else /* CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK */
|
||||
static void fips140_init_integrity_debug_files(const void *text, int textsize,
|
||||
const void *rodata,
|
||||
int rodatasize)
|
||||
{
|
||||
}
|
||||
#endif /* !CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK */
|
||||
|
||||
extern struct {
|
||||
u32 offset;
|
||||
u32 count;
|
||||
} fips140_rela_text, fips140_rela_rodata;
|
||||
|
||||
static bool __init check_fips140_module_hmac(void)
|
||||
{
|
||||
struct crypto_shash *tfm = NULL;
|
||||
SHASH_DESC_ON_STACK(desc, dontcare);
|
||||
u8 digest[SHA256_DIGEST_SIZE];
|
||||
void *textcopy, *rodatacopy;
|
||||
int textsize, rodatasize;
|
||||
bool ok = false;
|
||||
int err;
|
||||
|
||||
textsize = &__fips140_text_end - &__fips140_text_start;
|
||||
rodatasize = &__fips140_rodata_end - &__fips140_rodata_start;
|
||||
|
||||
pr_info("text size : 0x%x\n", textsize);
|
||||
pr_info("rodata size: 0x%x\n", rodatasize);
|
||||
|
||||
textcopy = kmalloc(textsize + rodatasize, GFP_KERNEL);
|
||||
if (!textcopy) {
|
||||
pr_err("Failed to allocate memory for copy of .text\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rodatacopy = textcopy + textsize;
|
||||
|
||||
memcpy(textcopy, fips140_text_start, textsize);
|
||||
memcpy(rodatacopy, fips140_rodata_start, rodatasize);
|
||||
|
||||
// apply the relocations in reverse on the copies of .text and .rodata
|
||||
unapply_text_relocations(textcopy, textsize,
|
||||
offset_to_ptr(&fips140_rela_text.offset),
|
||||
fips140_rela_text.count);
|
||||
|
||||
unapply_rodata_relocations(rodatacopy, rodatasize,
|
||||
offset_to_ptr(&fips140_rela_rodata.offset),
|
||||
fips140_rela_rodata.count);
|
||||
|
||||
unapply_scs_patch(textcopy, textsize);
|
||||
|
||||
fips140_init_integrity_debug_files(textcopy, textsize,
|
||||
rodatacopy, rodatasize);
|
||||
|
||||
fips140_inject_integrity_failure(textcopy);
|
||||
|
||||
tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
pr_err("failed to allocate hmac tfm (%ld)\n", PTR_ERR(tfm));
|
||||
tfm = NULL;
|
||||
goto out;
|
||||
}
|
||||
desc->tfm = tfm;
|
||||
|
||||
pr_info("using '%s' for integrity check\n",
|
||||
crypto_shash_driver_name(tfm));
|
||||
|
||||
err = crypto_shash_setkey(tfm, fips140_integ_hmac_key,
|
||||
strlen(fips140_integ_hmac_key)) ?:
|
||||
crypto_shash_init(desc) ?:
|
||||
crypto_shash_update(desc, textcopy, textsize) ?:
|
||||
crypto_shash_finup(desc, rodatacopy, rodatasize, digest);
|
||||
|
||||
/* Zeroizing this is important; see the comment below. */
|
||||
shash_desc_zero(desc);
|
||||
|
||||
if (err) {
|
||||
pr_err("failed to calculate hmac shash (%d)\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (memcmp(digest, fips140_integ_hmac_digest, sizeof(digest))) {
|
||||
pr_err("provided_digest : %*phN\n", (int)sizeof(digest),
|
||||
fips140_integ_hmac_digest);
|
||||
|
||||
pr_err("calculated digest: %*phN\n", (int)sizeof(digest),
|
||||
digest);
|
||||
goto out;
|
||||
}
|
||||
ok = true;
|
||||
out:
|
||||
/*
|
||||
* FIPS 140-3 requires that all "temporary value(s) generated during the
|
||||
* integrity test" be zeroized (ref: FIPS 140-3 IG 9.7.B). There is no
|
||||
* technical reason to do this given that these values are public
|
||||
* information, but this is the requirement so we follow it.
|
||||
*/
|
||||
crypto_free_shash(tfm);
|
||||
memzero_explicit(digest, sizeof(digest));
|
||||
kfree_sensitive(textcopy);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void fips140_sha256(void *p, const u8 *data, unsigned int len, u8 *out,
|
||||
int *hook_inuse)
|
||||
{
|
||||
sha256(data, len, out);
|
||||
*hook_inuse = 1;
|
||||
}
|
||||
|
||||
static void fips140_aes_expandkey(void *p, struct crypto_aes_ctx *ctx,
|
||||
const u8 *in_key, unsigned int key_len,
|
||||
int *err)
|
||||
{
|
||||
*err = aes_expandkey(ctx, in_key, key_len);
|
||||
}
|
||||
|
||||
static void fips140_aes_encrypt(void *priv, const struct crypto_aes_ctx *ctx,
|
||||
u8 *out, const u8 *in, int *hook_inuse)
|
||||
{
|
||||
aes_encrypt(ctx, out, in);
|
||||
*hook_inuse = 1;
|
||||
}
|
||||
|
||||
static void fips140_aes_decrypt(void *priv, const struct crypto_aes_ctx *ctx,
|
||||
u8 *out, const u8 *in, int *hook_inuse)
|
||||
{
|
||||
aes_decrypt(ctx, out, in);
|
||||
*hook_inuse = 1;
|
||||
}
|
||||
|
||||
static bool update_fips140_library_routines(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_trace_android_vh_sha256(fips140_sha256, NULL) ?:
|
||||
register_trace_android_vh_aes_expandkey(fips140_aes_expandkey, NULL) ?:
|
||||
register_trace_android_vh_aes_encrypt(fips140_aes_encrypt, NULL) ?:
|
||||
register_trace_android_vh_aes_decrypt(fips140_aes_decrypt, NULL);
|
||||
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
/* Initialize the FIPS 140 module */
|
||||
static int __init fips140_init(void)
|
||||
{
|
||||
const initcall_entry_t *initcall;
|
||||
|
||||
pr_info("loading " FIPS140_MODULE_NAME " " FIPS140_MODULE_VERSION "\n");
|
||||
fips140_init_thread = current;
|
||||
|
||||
unregister_existing_fips140_algos();
|
||||
|
||||
/* iterate over all init routines present in this module and call them */
|
||||
for (initcall = fips140_initcalls_start + 1;
|
||||
initcall < &__fips140_initcalls_end;
|
||||
initcall++) {
|
||||
initcall_t init = offset_to_ptr(initcall);
|
||||
int err = init();
|
||||
|
||||
/*
|
||||
* ENODEV is expected from initcalls that only register
|
||||
* algorithms that depend on non-present CPU features. Besides
|
||||
* that, errors aren't expected here.
|
||||
*/
|
||||
if (err && err != -ENODEV) {
|
||||
pr_err("initcall %ps() failed: %d\n", init, err);
|
||||
goto panic;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fips140_run_selftests())
|
||||
goto panic;
|
||||
|
||||
if (!fips140_verify_no_extra_unregistrations())
|
||||
goto panic;
|
||||
|
||||
/*
|
||||
* It may seem backward to perform the integrity check last, but this
|
||||
* is intentional: the check itself uses hmac(sha256) which is one of
|
||||
* the algorithms that are replaced with versions from this module, and
|
||||
* the integrity check must use the replacement version. Also, to be
|
||||
* ready for FIPS 140-3, the integrity check algorithm must have already
|
||||
* been self-tested.
|
||||
*/
|
||||
|
||||
if (!check_fips140_module_hmac()) {
|
||||
if (!IS_ENABLED(CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK)) {
|
||||
pr_crit("integrity check failed -- giving up!\n");
|
||||
goto panic;
|
||||
}
|
||||
pr_crit("ignoring integrity check failure due to debug mode\n");
|
||||
} else {
|
||||
pr_info("integrity check passed\n");
|
||||
}
|
||||
|
||||
complete_all(&fips140_tests_done);
|
||||
|
||||
if (!update_fips140_library_routines())
|
||||
goto panic;
|
||||
|
||||
if (!fips140_eval_testing_init())
|
||||
goto panic;
|
||||
|
||||
pr_info("module successfully loaded\n");
|
||||
return 0;
|
||||
|
||||
panic:
|
||||
panic("FIPS 140 module load failure");
|
||||
}
|
||||
|
||||
module_init(fips140_init);
|
||||
|
||||
MODULE_IMPORT_NS(CRYPTO_INTERNAL);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
/*
|
||||
* Below are copies of some selected "crypto-related" helper functions that are
|
||||
* used by fips140.ko but are not already built into it, due to them being
|
||||
* defined in a file that cannot easily be built into fips140.ko (e.g.,
|
||||
* crypto/algapi.c) instead of one that can (e.g., most files in lib/).
|
||||
*
|
||||
* There is no hard rule about what needs to be included here, as this is for
|
||||
* FIPS certifiability, not any technical reason. FIPS modules are supposed to
|
||||
* implement the "crypto" themselves, but to do so they are allowed to call
|
||||
* non-cryptographic helper functions from outside the module. Something like
|
||||
* memcpy() is "clearly" non-cryptographic. However, there is is ambiguity
|
||||
* about functions like crypto_inc() which aren't cryptographic by themselves,
|
||||
* but are more closely associated with cryptography than e.g. memcpy(). To err
|
||||
* on the side of caution, we define copies of some selected functions below so
|
||||
* that calls to them from within fips140.ko will remain in fips140.ko.
|
||||
*/
|
||||
|
||||
static inline void crypto_inc_byte(u8 *a, unsigned int size)
|
||||
{
|
||||
u8 *b = (a + size);
|
||||
u8 c;
|
||||
|
||||
for (; size; size--) {
|
||||
c = *--b + 1;
|
||||
*b = c;
|
||||
if (c)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void crypto_inc(u8 *a, unsigned int size)
|
||||
{
|
||||
__be32 *b = (__be32 *)(a + size);
|
||||
u32 c;
|
||||
|
||||
if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) ||
|
||||
IS_ALIGNED((unsigned long)b, __alignof__(*b)))
|
||||
for (; size >= 4; size -= 4) {
|
||||
c = be32_to_cpu(*--b) + 1;
|
||||
*b = cpu_to_be32(c);
|
||||
if (likely(c))
|
||||
return;
|
||||
}
|
||||
|
||||
crypto_inc_byte(a, size);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
Executable
+125
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Generate most of the test vectors for the FIPS 140 cryptographic self-tests.
|
||||
#
|
||||
# Usage:
|
||||
# tools/crypto/gen_fips140_testvecs.py > crypto/fips140-generated-testvecs.h
|
||||
#
|
||||
# Prerequisites:
|
||||
# Debian: apt-get install python3-pycryptodome python3-cryptography
|
||||
# Arch Linux: pacman -S python-pycryptodomex python-cryptography
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
import os
|
||||
|
||||
import Cryptodome.Cipher.AES
|
||||
import Cryptodome.Util.Counter
|
||||
|
||||
import cryptography.hazmat.primitives.ciphers
|
||||
import cryptography.hazmat.primitives.ciphers.algorithms
|
||||
import cryptography.hazmat.primitives.ciphers.modes
|
||||
|
||||
scriptname = os.path.basename(__file__)
|
||||
|
||||
message = bytes('This is a 32-byte test message.\0', 'ascii')
|
||||
aes_key = bytes('128-bit AES key\0', 'ascii')
|
||||
aes_xts_key = bytes('This is an AES-128-XTS key.\0\0\0\0\0', 'ascii')
|
||||
aes_iv = bytes('ABCDEFGHIJKLMNOP', 'ascii')
|
||||
assoc = bytes('associated data string', 'ascii')
|
||||
hmac_key = bytes('128-bit HMAC key', 'ascii')
|
||||
|
||||
def warn_generated():
|
||||
print(f'''/*
|
||||
* This header was automatically generated by {scriptname}.
|
||||
* Don't edit it directly.
|
||||
*/''')
|
||||
|
||||
def is_string_value(value):
|
||||
return (value.isascii() and
|
||||
all(c == '\x00' or c.isprintable() for c in str(value, 'ascii')))
|
||||
|
||||
def format_value(value, is_string):
|
||||
if is_string:
|
||||
return value
|
||||
hexstr = ''
|
||||
for byte in value:
|
||||
hexstr += f'\\x{byte:02x}'
|
||||
return hexstr
|
||||
|
||||
def print_value(name, value):
|
||||
is_string = is_string_value(value)
|
||||
hdr = f'static const u8 fips_{name}[{len(value)}] __initconst ='
|
||||
print(hdr, end='')
|
||||
if is_string:
|
||||
value = str(value, 'ascii').rstrip('\x00')
|
||||
chars_per_byte = 1
|
||||
else:
|
||||
chars_per_byte = 4
|
||||
bytes_per_line = 64 // chars_per_byte
|
||||
|
||||
if len(hdr) + (chars_per_byte * len(value)) + 4 <= 80:
|
||||
print(f' "{format_value(value, is_string)}"', end='')
|
||||
else:
|
||||
for chunk in [value[i:i+bytes_per_line]
|
||||
for i in range(0, len(value), bytes_per_line)]:
|
||||
print(f'\n\t"{format_value(chunk, is_string)}"', end='')
|
||||
print(';')
|
||||
print('')
|
||||
|
||||
def generate_aes_testvecs():
|
||||
print_value('aes_key', aes_key)
|
||||
print_value('aes_iv', aes_iv)
|
||||
|
||||
cbc = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_CBC,
|
||||
iv=aes_iv)
|
||||
print_value('aes_cbc_ciphertext', cbc.encrypt(message))
|
||||
|
||||
ecb = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_ECB)
|
||||
print_value('aes_ecb_ciphertext', ecb.encrypt(message))
|
||||
|
||||
ctr = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_CTR,
|
||||
nonce=bytes(), initial_value=aes_iv)
|
||||
print_value('aes_ctr_ciphertext', ctr.encrypt(message))
|
||||
|
||||
print_value('aes_gcm_assoc', assoc)
|
||||
gcm = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_GCM,
|
||||
nonce=aes_iv[:12], mac_len=16)
|
||||
gcm.update(assoc)
|
||||
raw_ciphertext, tag = gcm.encrypt_and_digest(message)
|
||||
print_value('aes_gcm_ciphertext', raw_ciphertext + tag)
|
||||
|
||||
# Unfortunately, pycryptodome doesn't support XTS, so for it we need to use
|
||||
# a different Python package (the "cryptography" package).
|
||||
print_value('aes_xts_key', aes_xts_key)
|
||||
xts = cryptography.hazmat.primitives.ciphers.Cipher(
|
||||
cryptography.hazmat.primitives.ciphers.algorithms.AES(aes_xts_key),
|
||||
cryptography.hazmat.primitives.ciphers.modes.XTS(aes_iv)).encryptor()
|
||||
ciphertext = xts.update(message) + xts.finalize()
|
||||
print_value('aes_xts_ciphertext', ciphertext)
|
||||
|
||||
cmac = Cryptodome.Hash.CMAC.new(aes_key, ciphermod=Cryptodome.Cipher.AES)
|
||||
cmac.update(message)
|
||||
print_value('aes_cmac_digest', cmac.digest())
|
||||
|
||||
def generate_sha_testvecs():
|
||||
print_value('hmac_key', hmac_key)
|
||||
for alg in ['sha1', 'sha256', 'hmac_sha256', 'sha512', 'sha3_256']:
|
||||
if alg.startswith('hmac_'):
|
||||
h = hmac.new(hmac_key, message, alg.removeprefix('hmac_'))
|
||||
else:
|
||||
h = hashlib.new(alg, message)
|
||||
print_value(f'{alg}_digest', h.digest())
|
||||
|
||||
print('/* SPDX-License-Identifier: GPL-2.0-only */')
|
||||
print('/* Copyright 2021 Google LLC */')
|
||||
print('')
|
||||
warn_generated()
|
||||
print('')
|
||||
print_value('message', message)
|
||||
generate_aes_testvecs()
|
||||
generate_sha_testvecs()
|
||||
warn_generated()
|
||||
Reference in New Issue
Block a user