Introduce CONFIG_CRYPTO_FIPS140_DUAL_VERSION to enable dual crypto module
versions within a single kernel build, allowing boot-time selection based on
FIPS mode status.

This configuration allows FIPS mode to use pre-compiled certified crypto
modules from external source, while regular mode uses freshly built kernel
crypto implementation for optimal performance and latest security features.

The implementation embeds both certified and non-certified fips140.ko
modules in vmlinux and adds new linker sections (.nonfips140_embedded,
.nonfips140_btf) for non-FIPS crypto module storage. It modifies
fips140-loader.c to select appropriate module at boot time based on
fips_enabled flag, updates build system to generate and embed both module
versions, and includes BTF support for both module variants when
CONFIG_DEBUG_INFO_BTF_MODULES is enabled.

For modular crypto algorithms (e.g., aes.ko), they are not automatically
duplicated. They should either be built-in to fips140.ko for automatic
duplication, or require userspace utilities like modprobe to handle
proper isolation between FIPS and non-FIPS modular crypto implementations.

Signed-off-by: Jay Wang <[email protected]>
---
 Makefile                        | 13 +++++++++++++
 arch/arm64/kernel/vmlinux.lds.S | 16 ++++++++++++++++
 arch/x86/kernel/vmlinux.lds.S   | 16 ++++++++++++++++
 crypto/fips140/Kconfig          | 24 ++++++++++++++++++++++++
 crypto/fips140/Makefile         |  5 ++++-
 crypto/fips140/fips140-loader.c | 28 +++++++++++++++++++++++++++-
 scripts/Makefile.vmlinux        | 29 +++++++++++++++++++++++++++--
 scripts/link-vmlinux.sh         |  6 ++++++
 8 files changed, 133 insertions(+), 4 deletions(-)

diff --git a/Makefile b/Makefile
index 7530009d8081..b3a9f7a17ddf 100644
--- a/Makefile
+++ b/Makefile
@@ -1293,6 +1293,9 @@ vmlinux: private _LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
 vmlinux: export LDFLAGS_vmlinux = $(_LDFLAGS_vmlinux)
 ifdef CONFIG_CRYPTO_FIPS140_EXTMOD
 vmlinux: crypto/fips140/fips140-embedded.o crypto/fips140/fips140-digest.o
+ifdef CONFIG_CRYPTO_FIPS140_DUAL_VERSION
+vmlinux: crypto/fips140/nonfips140-embedded.o
+endif
 fips140_build = .
 ifeq ($(CONFIG_CRYPTO_FIPS140_EXTMOD_SOURCE),y)
 fips140_build = fips140_build
@@ -1302,6 +1305,14 @@ crypto/fips140/fips140-embedded.o: fips140-ready
        @$(LD) -r -b binary -o $@ $(fips140_build)/crypto/fips140/fips140.ko
        @$(OBJCOPY) --rename-section .data=.fips140_module_data $@
 
+ifdef CONFIG_CRYPTO_FIPS140_DUAL_VERSION
+crypto/fips140/nonfips140-embedded.o: fips140-ready
+       @echo "  LD      $@"
+       @$(LD) -r -b binary -o $@ crypto/fips140/fips140.ko
+       @$(OBJCOPY) --rename-section .data=.nonfips140_module_data \
+               --prefix-symbols nonfips140_ $@
+endif
+
 crypto/fips140/.fips140.hmac: crypto/fips140/fips140-embedded.o
        @echo "  HMAC    $@"
        @hmac_key=$$(awk -F'"' '/^CONFIG_CRYPTO_FIPS140_HMAC_KEY=/{print $$2}' 
.config); \
@@ -1319,9 +1330,11 @@ fips140-ready: crypto/fips140/fips140.o 
crypto/fips140/.fips140.order crypto/fip
 ifneq ($(KBUILD_MODPOST_NOFINAL),1)
        $(Q)$(MAKE) KBUILD_MODULES=y crypto-module-gen=1 -f 
$(srctree)/scripts/Makefile.modfinal
 endif
+ifndef CONFIG_CRYPTO_FIPS140_DUAL_VERSION
 ifeq ($(CONFIG_CRYPTO_FIPS140_EXTMOD_SOURCE),y)
        cp "$(fips140_build)/crypto/fips140/fips140.ko" 
crypto/fips140/fips140.ko;
 endif
+endif
 
 # Generate fips140.o from crypto-module.a files
 crypto/fips140/fips140.o: crypto-module.a FORCE
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 41223fa3f14e..0722e07c5551 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -222,6 +222,22 @@ SECTIONS
                __stop_fips140_btf = .;
        }
 #endif
+#ifdef CONFIG_CRYPTO_FIPS140_DUAL_VERSION
+       .nonfips140_embedded : {
+               . = ALIGN(8);
+               _binary_nonfips140_ko_start = .;
+               KEEP(*(.nonfips140_module_data))
+               _binary_nonfips140_ko_end = .;
+       }
+#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+       .nonfips140_btf : {
+               . = ALIGN(8);
+               __start_nonfips140_btf = .;
+               KEEP(*(.nonfips140_btf))
+               __stop_nonfips140_btf = .;
+       }
+#endif
+#endif
 #endif
 
        HYPERVISOR_RODATA_SECTIONS
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index e07c1b5c52cf..aa1b97d7aabd 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -193,6 +193,22 @@ SECTIONS
                __stop_fips140_btf = .;
        }
 #endif
+#ifdef CONFIG_CRYPTO_FIPS140_DUAL_VERSION
+       .nonfips140_embedded : AT(ADDR(.nonfips140_embedded) - LOAD_OFFSET) {
+               . = ALIGN(8);
+               _binary_nonfips140_ko_start = .;
+               KEEP(*(.nonfips140_module_data))
+               _binary_nonfips140_ko_end = .;
+       }
+#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+       .nonfips140_btf : AT(ADDR(.nonfips140_btf) - LOAD_OFFSET) {
+               . = ALIGN(8);
+               __start_nonfips140_btf = .;
+               KEEP(*(.nonfips140_btf))
+               __stop_nonfips140_btf = .;
+       }
+#endif
+#endif
 #endif
 
        /* Data */
diff --git a/crypto/fips140/Kconfig b/crypto/fips140/Kconfig
index 68b877f0dbab..7d8997aa1094 100644
--- a/crypto/fips140/Kconfig
+++ b/crypto/fips140/Kconfig
@@ -42,3 +42,27 @@ config CRYPTO_FIPS140_EXTMOD_SOURCE
            - fips140_build/crypto/sha256.ko
          
          If unsure, say N.
+config CRYPTO_FIPS140_DUAL_VERSION
+       bool "Enable dual crypto versions for FIPS and regular modes"
+       depends on CRYPTO_FIPS140_EXTMOD && CRYPTO_FIPS140_EXTMOD_SOURCE
+       default n
+       help
+         Enable keeping two crypto module versions in the same kernel build
+         for boot-time switching based on FIPS mode status. This allows:
+         - Non-FIPS users: Get latest crypto algorithms built from current
+           kernel sources for optimal performance and security features
+         - FIPS users: Get pre-compiled certified crypto modules that have
+           undergone formal validation and certification processes
+
+         When enabled:
+
+         For core fips140.ko:
+         - FIPS mode: Uses certified module from CRYPTO_FIPS140_EXTMOD_SOURCE
+         - Regular mode: Uses freshly built kernel crypto implementation
+
+         For modular algorithms (e.g., aes.ko), they are not duplicated
+         automatically. Either make them built-in to be included into
+         fips140.ko for automatic duplication, or require OS utilities such
+         as `modprobe` to correctly isolate modular cryptos in filesystems.
+
+         If unsure, say N.
\ No newline at end of file
diff --git a/crypto/fips140/Makefile b/crypto/fips140/Makefile
index ac8ae42eb0fa..c99bf2948432 100644
--- a/crypto/fips140/Makefile
+++ b/crypto/fips140/Makefile
@@ -15,4 +15,7 @@ $(obj)/fips140-api-fips.o: $(src)/fips140-api.c FORCE
 CFLAGS_fips140-api-main.o += -I$(srctree)
 CFLAGS_fips140-api-fips.o += -I$(srctree)
 
-clean-files:= .fips140.order .fips140.symvers .fips140.hmac .fips140.ko.btf
\ No newline at end of file
+clean-files:= .fips140.order .fips140.symvers .fips140.hmac .fips140.ko.btf
+ifdef CONFIG_CRYPTO_FIPS140_DUAL_VERSION
+clean-files += .nonfips140.ko.btf
+endif
\ No newline at end of file
diff --git a/crypto/fips140/fips140-loader.c b/crypto/fips140/fips140-loader.c
index 13c82ffdc65b..826075928723 100644
--- a/crypto/fips140/fips140-loader.c
+++ b/crypto/fips140/fips140-loader.c
@@ -11,12 +11,20 @@
 #include <linux/elf.h>
 #include <linux/kthread.h>
 #include <linux/wait.h>
+#include <linux/fips.h>
 
 extern const u8 _binary_fips140_ko_start[];
 extern const u8 _binary_fips140_ko_end[];
 extern const u8 _binary_fips140_hmac_start[];
 extern const u8 _binary_fips140_hmac_end[];
 
+#ifdef CONFIG_CRYPTO_FIPS140_DUAL_VERSION
+/* For non-FIPS mode: no module signature/HMAC is required,
+ * so only include binary start/end address without module sig address */
+extern const u8 _binary_nonfips140_ko_start[];
+extern const u8 _binary_nonfips140_ko_end[];
+#endif
+
 const u8 *_binary_crypto_ko_start;
 EXPORT_SYMBOL_GPL(_binary_crypto_ko_start);
 const u8 *_binary_crypto_ko_end;
@@ -29,6 +37,10 @@ EXPORT_SYMBOL_GPL(_binary_crypto_hmac_end);
 #ifdef CONFIG_DEBUG_INFO_BTF_MODULES
 extern const u8 __start_fips140_btf[];
 extern const u8 __stop_fips140_btf[];
+#ifdef CONFIG_CRYPTO_FIPS140_DUAL_VERSION
+extern const u8 __start_nonfips140_btf[];
+extern const u8 __stop_nonfips140_btf[];
+#endif
 const u8 *__start_crypto_btf;
 const u8 *__stop_crypto_btf;
 #endif
@@ -49,11 +61,25 @@ static void load_prepare(void)
        _binary_crypto_ko_end = _binary_fips140_ko_end;
        _binary_crypto_hmac_start = _binary_fips140_hmac_start;
        _binary_crypto_hmac_end = _binary_fips140_hmac_end;
-       
+
 #ifdef CONFIG_DEBUG_INFO_BTF_MODULES
        __start_crypto_btf = __start_fips140_btf;
        __stop_crypto_btf = __stop_fips140_btf;
 #endif
+
+#ifdef CONFIG_CRYPTO_FIPS140_DUAL_VERSION
+       if (!fips_enabled) {
+               _binary_crypto_ko_start = _binary_nonfips140_ko_start;
+               _binary_crypto_ko_end = _binary_nonfips140_ko_end;
+               _binary_crypto_hmac_start = NULL;
+               _binary_crypto_hmac_end = NULL;
+
+#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+               __start_crypto_btf = __start_nonfips140_btf;
+               __stop_crypto_btf = __stop_nonfips140_btf;
+#endif
+               }
+#endif
 }
 
 static int __init fips_loader_init(void)
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index b30d65f8b6b3..996d016e518c 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -81,7 +81,18 @@ ifdef CONFIG_DEBUG_INFO_BTF_MODULES
        cp crypto/fips140/.fips140.ko.btf crypto/fips140/.fips140.ko.btf.first; 
\
        rm -f crypto/fips140/fips140.ko.tmp; \
        $(LD) -r -b binary -o crypto/fips140/fips140_btf.o 
crypto/fips140/.fips140.ko.btf; \
-       $(OBJCOPY) --rename-section .data=.fips140_btf 
crypto/fips140/fips140_btf.o; \
+       $(OBJCOPY) --rename-section .data=.fips140_btf 
crypto/fips140/fips140_btf.o
+ifdef CONFIG_CRYPTO_FIPS140_DUAL_VERSION
+      cmd_link_vmlinux += ; \
+       cp crypto/fips140/fips140.ko crypto/fips140/nonfips140.ko.tmp; \
+       LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) 
$(MODULE_PAHOLE_FLAGS) --btf_base $@ crypto/fips140/nonfips140.ko.tmp; \
+       $(RESOLVE_BTFIDS) -b $@ crypto/fips140/nonfips140.ko.tmp; \
+       $(OBJCOPY) --dump-section=.BTF=crypto/fips140/.nonfips140.ko.btf 
crypto/fips140/nonfips140.ko.tmp; \
+       rm -f crypto/fips140/nonfips140.ko.tmp; \
+       $(LD) -r -b binary -o crypto/fips140/nonfips140_btf.o 
crypto/fips140/.nonfips140.ko.btf; \
+       $(OBJCOPY) --rename-section .data=.nonfips140_btf --prefix-symbols 
nonfips140_ crypto/fips140/nonfips140_btf.o
+endif
+         cmd_link_vmlinux += ; \
        rm -f $@; \
        FIPS140_BTF_RELINK=1 $< "$(LD)" "$(KBUILD_LDFLAGS)" 
"$(LDFLAGS_vmlinux)" "$@"; \
        cp $(fips140_build)/crypto/fips140/fips140.ko 
crypto/fips140/fips140.ko.tmp2; \
@@ -90,13 +101,27 @@ ifdef CONFIG_DEBUG_INFO_BTF_MODULES
        $(OBJCOPY) --dump-section=.BTF=crypto/fips140/.fips140.ko.btf.second 
crypto/fips140/fips140.ko.tmp2; \
        rm -f crypto/fips140/fips140.ko.tmp2; \
        diff crypto/fips140/.fips140.ko.btf.first 
crypto/fips140/.fips140.ko.btf.second >/dev/null || echo "Module BTF differs"; \
-       rm -f crypto/fips140/.fips140.ko.btf.first 
crypto/fips140/.fips140.ko.btf.second; \
+       rm -f crypto/fips140/.fips140.ko.btf.first 
crypto/fips140/.fips140.ko.btf.second
+ifdef CONFIG_CRYPTO_FIPS140_DUAL_VERSION
+      cmd_link_vmlinux += ; \
+       cp crypto/fips140/fips140.ko crypto/fips140/nonfips140.ko.tmp2; \
+       LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) 
$(MODULE_PAHOLE_FLAGS) --btf_base $@ crypto/fips140/nonfips140.ko.tmp2; \
+       $(RESOLVE_BTFIDS) -b $@ crypto/fips140/nonfips140.ko.tmp2; \
+       $(OBJCOPY) --dump-section=.BTF=crypto/fips140/.nonfips140.ko.btf.second 
crypto/fips140/nonfips140.ko.tmp2; \
+       rm -f crypto/fips140/nonfips140.ko.tmp2; \
+       diff crypto/fips140/.nonfips140.ko.btf 
crypto/fips140/.nonfips140.ko.btf.second >/dev/null || echo "Nonfips140 Module 
BTF differs"; \
+       rm -f crypto/fips140/.nonfips140.ko.btf.second
+endif
+         cmd_link_vmlinux += ; \
        $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
 endif
 endif
 
 ifdef CONFIG_CRYPTO_FIPS140_EXTMOD
 fips140-deps := crypto/fips140/fips140-embedded.o 
crypto/fips140/fips140-digest.o
+ifdef CONFIG_CRYPTO_FIPS140_DUAL_VERSION
+fips140-deps += crypto/fips140/nonfips140-embedded.o
+endif
 endif
 
 targets += vmlinux.unstripped .vmlinux.export.o
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 37c9b8576ec7..43a272e8d3a4 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -78,8 +78,14 @@ vmlinux_link()
 
        if is_enabled CONFIG_CRYPTO_FIPS140_EXTMOD; then
                objs="${objs} crypto/fips140/fips140-embedded.o 
crypto/fips140/fips140-digest.o"
+               if is_enabled CONFIG_CRYPTO_FIPS140_DUAL_VERSION; then
+                       objs="${objs} crypto/fips140/nonfips140-embedded.o"
+               fi
                if is_enabled CONFIG_DEBUG_INFO_BTF_MODULES && [ -n 
"${FIPS140_BTF_RELINK}" ] && [ -f crypto/fips140/fips140_btf.o ]; then
                        objs="${objs} crypto/fips140/fips140_btf.o"
+                       if is_enabled CONFIG_CRYPTO_FIPS140_DUAL_VERSION && [ 
-f crypto/fips140/nonfips140_btf.o ]; then
+                               objs="${objs} crypto/fips140/nonfips140_btf.o"
+                       fi
                fi
        fi
 
-- 
2.47.3


Reply via email to