This code is partly based on the existing crypto/fips.c code and partly on the Android FIPS 140 module [1]; __fips_initcalls/run_initcalls() based loosely on code by Jay Wang <wanjay@xxxxxxxxxx>. [1]: https://android.googlesource.com/kernel/common/+/1ca1130ec62d/crypto/fips140-module.c Signed-off-by: Vegard Nossum <vegard.nossum@xxxxxxxxxx> --- crypto/Kconfig | 16 +-- fips140/Kconfig | 174 +++++++++++++++++++++++++++++++++ fips140/Makefile | 183 ++++++++++++++++++++++++++++++++++ fips140/fips140-glue.c | 216 +++++++++++++++++++++++++++++++++++++++++ fips140/fips140.lds | 9 ++ 5 files changed, 583 insertions(+), 15 deletions(-) create mode 100644 fips140/Kconfig create mode 100644 fips140/Makefile create mode 100644 fips140/fips140-glue.c create mode 100644 fips140/fips140.lds diff --git a/crypto/Kconfig b/crypto/Kconfig index a2696ea30bde..f9b2f87ad04b 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -54,21 +54,7 @@ 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_EXTMOD - bool "FIPS 140-3 external module" - depends on CRYPTO_FIPS - help - Support loading a prebuilt FIPS 140-3 cryptographic module. - -config CRYPTO_FIPS140_HMAC_KEY - string "FIPS 140-3 external module HMAC key" - depends on CRYPTO_FIPS140_EXTMOD - default "Sphinx of black quartz, judge my vow" - help - This is the HMAC key used to build and verify the integrity of - the FIPS module. - - Must be at least 14 characters. +source "fips140/Kconfig" config CRYPTO_ALGAPI tristate diff --git a/fips140/Kconfig b/fips140/Kconfig new file mode 100644 index 000000000000..188c5f360eab --- /dev/null +++ b/fips140/Kconfig @@ -0,0 +1,174 @@ +# NOTE: the structure of this file mirrors that of crypto/Kconfig +# and crypto/asymmetric/keys/Kconfig + +config CRYPTO_FIPS140_EXTMOD + bool "FIPS 140-3 external module" + depends on MODULES + depends on CRYPTO_FIPS + help + Support loading a prebuilt FIPS 140-3 cryptographic module. + +if CRYPTO_FIPS140_EXTMOD + +config CRYPTO_FIPS140_HMAC_KEY + string "FIPS 140-3 external module HMAC key" + default "Sphinx of black quartz, judge my vow" + help + This is the HMAC key used to build and verify the integrity of + the FIPS module. + + Must be at least 14 characters. + +config CRYPTO_FIPS140_PCRYPT + bool + default CRYPTO_PCRYPT + +menu "FIPS 140 external module algorithms" + depends on CRYPTO_FIPS140_EXTMOD + +# +# Public-key cryptography +# + +config CRYPTO_FIPS140_RSA + bool "RSA (Rivest-Shamir-Adleman)" + default CRYPTO_RSA + +config CRYPTO_FIPS140_DH + bool "DH (Diffie-Hellman)" + default CRYPTO_DH + +config CRYPTO_FIPS140_ECC + bool + default CRYPTO_ECC + +config CRYPTO_FIPS140_ECDH + bool "ECDH (Elliptic Curve Diffie-Hellman)" + select CRYPTO_FIPS140_ECC + default CRYPTO_ECDH + +config CRYPTO_FIPS140_ECDSA + bool "ECDSA (Elliptic Curve Digital Signature Algorithm)" + default CRYPTO_ECDSA + +# +# Block ciphers +# + +config CRYPTO_FIPS140_AES + bool "AES (Advanced Encryption Standard)" + default CRYPTO_AES + +# +# Length-preserving ciphers and modes +# + +config CRYPTO_FIPS140_CBC + bool "CBC (Cipher Block Chaining)" + default CRYPTO_CBC + +config CRYPTO_FIPS140_CTR + bool "CTR (Counter)" + default CRYPTO_CTR + +config CRYPTO_FIPS140_ECB + bool "ECB (Electronic Codebook)" + default CRYPTO_ECB + +config CRYPTO_FIPS140_XTS + bool "XTS (XOR Encrypt XOR with ciphertext stealing)" + default CRYPTO_XTS + +# +# AEAD (authenticated encryption with associated data) ciphers +# + +config CRYPTO_FIPS140_CCM + bool "CCM (Counter with Cipher Block Chaining-MAC)" + default CRYPTO_CCM + +config CRYPTO_FIPS140_GCM + bool "GCM (Galois/Counter Mode) and GMAC (GCM MAC)" + default CRYPTO_GCM + +config CRYPTO_FIPS140_GENIV + bool + default CRYPTO_GENIV + +config CRYPTO_FIPS140_SEQIV + bool "Sequence Number IV Generator" + select CRYPTO_FIPS140_GENIV + default CRYPTO_SEQIV + +config CRYPTO_FIPS140_ECHAINIV + bool "Encrypted Chain IV Generator" + select CRYPTO_FIPS140_GENIV + default CRYPTO_ECHAINIV + +config CRYPTO_FIPS140_ESSIV + bool "Encrypted Salt-Sector IV Generator" + default CRYPTO_ESSIV + +# +# Hashes, digests, and MACs +# + +config CRYPTO_FIPS140_CMAC + bool "CMAC (Cipher-based MAC)" + default CRYPTO_CMAC + +config CRYPTO_FIPS140_HMAC + bool "HMAC (Keyed-Hash MAC)" + default CRYPTO_HMAC + +config CRYPTO_FIPS140_SHA256 + bool "SHA-244 and SHA-256" + default CRYPTO_SHA256 + +config CRYPTO_FIPS140_SHA512 + bool "SHA-384 and SHA-512" + default CRYPTO_SHA512 + +config CRYPTO_FIPS140_SHA3 + bool "SHA-3" + default CRYPTO_SHA3 + +# +# Random number generation +# + +config CRYPTO_FIPS140_JITTERENTROPY + bool "CPU Jitter Non-Deterministic RNG (Random Number Generator)" + select CRYPTO_FIPS140_SHA3 + +# +# Asymmetric keys +# + +config FIPS140_ASYMMETRIC_KEY_TYPE + bool "Asymmetric (public-key cryptographic) key type" + default ASYMMETRIC_KEY_TYPE + +if FIPS140_ASYMMETRIC_KEY_TYPE + +config FIPS140_ASYMMETRIC_PUBLIC_KEY_SUBTYPE + bool "Asymmetric public-key crypto algorithm subtype" + default ASYMMETRIC_PUBLIC_KEY_SUBTYPE + +config FIPS140_X509_CERTIFICATE_PARSER + bool "X.509 certificate parser" + default X509_CERTIFICATE_PARSER + +config FIPS140_PKCS8_PRIVATE_KEY_PARSER + bool "PKCS#8 private key parser" + default PKCS8_PRIVATE_KEY_PARSER + +config FIPS140_PKCS7_MESSAGE_PARSER + bool "PKCS#7 message parser" + default PKCS7_MESSAGE_PARSER + +endif + +endmenu + +endif # if CRYPTO_FIPS140_EXTMOD diff --git a/fips140/Makefile b/fips140/Makefile new file mode 100644 index 000000000000..47365f1b3e27 --- /dev/null +++ b/fips140/Makefile @@ -0,0 +1,183 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2014 Linaro Ltd <ard.biesheuvel@xxxxxxxxxx> +# Copyright © 2025, Oracle and/or its affiliates. +# + +# +# Global options +# + +# make sure both .c and .S files see this +subdir-ccflags-y += -DFIPS_MODULE=1 +subdir-asflags-y += -DFIPS_MODULE=1 + +# +# Glue +# + +fips140-y := fips140-glue.o + +# +# crypto/Makefile +# + +fips140-y += crypto/algapi.o +fips140-y += crypto/api.o +fips140-y += crypto/ahash.o +fips140-y += crypto/cryptd.o +fips140-y += crypto/shash.o +fips140-y += crypto/proc.o + +# cryptomgr.ko +fips140-y += crypto/algboss.o crypto/testmgr.o + +fips140-$(CONFIG_CRYPTO_FIPS140_SHA3) += crypto/sha3_generic.o + +# jitterentropy_rng.ko +CFLAGS_crypto/jitterentropy.o = -O0 +KASAN_SANITIZE_crypto/jitterentropy.o = n +UBSAN_SANITIZE_crypto/jitterentropy.o = n +fips140-$(CONFIG_CRYPTO_FIPS140_JITTERENTROPY) += crypto/jitterentropy.o crypto/jitterentropy-kcapi.o + +fips140-$(CONFIG_CRYPTO_FIPS140_SHA512) += crypto/sha512.o + +fips140-$(CONFIG_CRYPTO_FIPS140_PCRYPT) += crypto/pcrypt.o +fips140-y += crypto/ghash-generic.o + +# crypto_simd.ko +fips140-y += crypto/simd.o + +fips140-$(CONFIG_CRYPTO_FIPS140_HMAC) += crypto/hmac.o +fips140-y += crypto/authenc.o +fips140-y += crypto/authencesn.o + +# rsa_generic.ko +fips140-$(CONFIG_CRYPTO_FIPS140_RSA) += $(addprefix crypto/, \ + rsapubkey.asn1.o \ + rsaprivkey.asn1.o \ + rsa.o \ + rsa_helper.o \ + rsa-pkcs1pad.o \ + rsassa-pkcs1.o \ +) + +fips140-$(CONFIG_CRYPTO_FIPS140_SHA256) += crypto/sha256.o + +fips140-$(CONFIG_CRYPTO_FIPS140_CMAC) += crypto/cmac.o +fips140-$(CONFIG_CRYPTO_FIPS140_AES) += crypto/aes_generic.o +fips140-$(CONFIG_CRYPTO_FIPS140_CBC) += crypto/cbc.o +fips140-$(CONFIG_CRYPTO_FIPS140_CCM) += crypto/ccm.o +fips140-$(CONFIG_CRYPTO_FIPS140_ECB) += crypto/ecb.o +fips140-$(CONFIG_CRYPTO_FIPS140_CTR) += crypto/ctr.o +fips140-$(CONFIG_CRYPTO_FIPS140_ECC) += crypto/ecc.o +fips140-y += crypto/drbg.o + +fips140-$(CONFIG_CRYPTO_FIPS140_ECDH) += $(addprefix crypto/, \ + ecdh.o \ + ecdh_helper.o \ +) + +# ecdsa_generic.ko +$(obj)/crypto/ecdsasignature.asn1.o: $(obj)/crypto/ecdsasignature.asn1.c $(obj)/crypto/ecdsasignature.asn1.h +$(obj)/crypto/ecdsa.o: $(obj)/crypto/ecdsasignature.asn1.h +fips140-$(CONFIG_CRYPTO_FIPS140_ECDSA) += $(addprefix crypto/, \ + ecdsa.o \ + ecdsa-x962.o \ + ecdsa-p1363.o \ + ecdsasignature.asn1.o \ +) + +# dh_generic.ko +fips140-$(CONFIG_CRYPTO_FIPS140_DH) += $(addprefix crypto/, \ + dh.o \ + dh_helper.o \ +) + +fips140-$(CONFIG_CRYPTO_FIPS140_GCM) += crypto/gcm.o +fips140-$(CRYPTO_FIPS140_XTS) += crypto/xts.o +fips140-y += crypto/tcrypt.o + +fips140-y += crypto/aead.o +fips140-y += crypto/kpp.o +fips140-y += crypto/cipher.o +fips140-y += crypto/akcipher.o +fips140-y += crypto/skcipher.o +fips140-y += crypto/lskcipher.o +fips140-y += crypto/rng.o +fips140-y += crypto/sig.o +fips140-$(CONFIG_CRYPTO_FIPS140_GENIV) += crypto/geniv.o +fips140-$(CONFIG_CRYPTO_FIPS140_SEQIV) += crypto/seqiv.o +fips140-$(CONFIG_CRYPTO_FIPS140_ECHAINIV) += crypto/echainiv.o +fips140-$(CONFIG_CRYPTO_FIPS140_ESSIV) += crypto/essiv.o + +# +# crypto/asymmetric_keys/Makefile +# + +fips140-$(CONFIG_FIPS140_ASYMMETRIC_KEY_TYPE) += \ + $(addprefix crypto/asymmetric_keys/, \ + asymmetric_type.o restrict.o signature.o) + +fips140-$(CONFIG_FIPS140_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += crypto/asymmetric_keys/public_key.o + +$(obj)/crypto/asymmetric_keys/x509_cert_parser.o: \ + $(addprefix $(obj)/crypto/asymmetric_keys/, \ + x509.asn1.h x509_akid.asn1.h) +$(obj)/crypto/asymmetric_keys/x509.asn1.o: \ + $(addprefix $(obj)/crypto/asymmetric_keys/, \ + x509.asn1.c x509.asn1.h) +$(obj)/crypto/asymmetric_keys/x509_akid.asn1.o: \ + $(addprefix $(obj)/crypto/asymmetric_keys/, \ + x509_akid.asn1.c x509_akid.asn1.h) + +fips140-$(CONFIG_FIPS140_X509_CERTIFICATE_PARSER) += \ + $(addprefix crypto/asymmetric_keys/, \ + x509_cert_parser.o \ + x509_loader.o \ + x509_akid.asn1.o \ + x509.asn1.o \ + x509_public_key.o \ + ) + +$(obj)/crypto/asymmetric_keys/pkcs8_parser.o: $(obj)/crypto/asymmetric_keys/pkcs8.asn1.h +$(obj)/crypto/asymmetric_keys/pkcs8-asn1.o: \ + $(addprefix $(obj)/crypto/asymmetric_keys/, \ + pkcs8.asn1.c pkcs8.asn1.h) + +fips140-$(CONFIG_FIPS140_PKCS8_PRIVATE_KEY_PARSER) += \ + $(addprefix crypto/asymmetric_keys/, \ + pkcs8_parser.o \ + pkcs8.asn1.o \ + ) + +fips140-$(CONFIG_FIPS140_PKCS7_MESSAGE_PARSER) += \ + $(addprefix crypto/asymmetric_keys/, \ + pkcs7_parser.o \ + pkcs7_trust.o \ + pkcs7_verify.o \ + pkcs7_key_type.o \ + pkcs7.asn1.o \ + ) + +$(obj)/crypto/asymmetric_keys/pkcs7_parser.o: $(obj)/crypto/asymmetric_keys/pkcs7.asn1.h +$(obj)/crypto/asymmetric_keys/pkcs7.asn1.o: \ + $(addprefix $(obj)/crypto/asymmetric_keys/, \ + pkcs7.asn1.c pkcs7.asn1.h) + +# +# lib/crypto/Makefile +# + +fips140-y += lib/crypto/aes.o +fips140-y += lib/crypto/aesgcm.o +fips140-y += lib/crypto/sha256.o +fips140-y += lib/crypto/sha512.o +fips140-y += lib/crypto/gf128mul.o +fips140-y += lib/crypto/memneq.o +fips140-y += lib/crypto/utils.o + +# Use custom linker script to collect initcalls +LDFLAGS_fips140.o += -T $(src)/fips140.lds + +obj-m += fips140.o diff --git a/fips140/fips140-glue.c b/fips140/fips140-glue.c new file mode 100644 index 000000000000..69db638c68d1 --- /dev/null +++ b/fips140/fips140-glue.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FIPS 140-3 and FIPS 200 support. + * + * Copyright (c) 2008 Neil Horman <nhorman@xxxxxxxxxxxxx> + * Copyright 2021 Google LLC + * Copyright (c) 2025, Oracle and/or its affiliates. + */ + +#include <generated/utsrelease.h> + +#include <linux/export.h> +#include <linux/fips.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/sysctl.h> + +#include <crypto/api.h> +#include <crypto/hash.h> + +#include "crypto/internal.h" + +#define FIPS_MODULE_NAME CONFIG_CRYPTO_FIPS_NAME +#ifdef CONFIG_CRYPTO_FIPS_CUSTOM_VERSION +#define FIPS_MODULE_VERSION CONFIG_CRYPTO_FIPS_VERSION +#else +#define FIPS_MODULE_VERSION UTS_RELEASE +#endif + +static char fips_name[] = FIPS_MODULE_NAME; +static char fips_version[] = FIPS_MODULE_VERSION; + +/* + * FIPS 140-2 prefers the use of HMAC with a public key over a plain hash. + */ +static const u8 fips140_integ_hmac_key[] = CONFIG_CRYPTO_FIPS140_HMAC_KEY; + +int fips_operational = 0; + +static int verify_integrity(void) +{ + int err; + + struct crypto_shash *tfm = NULL; + SHASH_DESC_ON_STACK(desc, dontcare); + u8 digest[SHA256_DIGEST_SIZE]; + + /* + * Verify integrity + */ + + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + if (IS_ERR(tfm)) + panic("FIPS 140: failed to allocate hmac tfm (%ld)\n", PTR_ERR(tfm)); + + desc->tfm = tfm; + pr_info("FIPS 140: 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)); + if (err) + panic("FIPS 140: crypto_shash_setkey() failed: %d\n", err); + + err = crypto_shash_init(desc); + if (err) + panic("FIPS 140: crypto_shash_init() failed: %d\n", err); + + /* Compute HMAC over the module's source memory */ + //err = crypto_shash_update(desc, THIS_MODULE->source_ptr, THIS_MODULE->source_len); + err = crypto_shash_update(desc, _binary_fips140_ko_start, _binary_fips140_ko_end - _binary_fips140_ko_start); + if (err) + panic("FIPS 140: crypto_shash_update() failed: %d\n", err); + + err = crypto_shash_final(desc, digest); + if (err) + panic("FIPS 140: crypto_shash_final() failed: %d\n", err); + + /* Zeroizing this is important; see the comment below. */ + shash_desc_zero(desc); + + if (err) + panic("FIPS 140: failed to calculate hmac shash (%d)\n", err); + + pr_info("FIPS 140: expected digest: %*phN\n", (int)sizeof(digest), _binary_fips140_hmac_start); + pr_info("FIPS 140: computed digest: %*phN\n", (int)sizeof(digest), digest); + + if (memcmp(digest, _binary_fips140_hmac_start, sizeof(digest))) + panic("FIPS 140: failed integrity check\n"); + + /* + * 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)); + + return 0; +} + +/* This technically is never supposed to change. */ +static int fips_standalone = 1; + +static struct ctl_table crypto_sysctl_table[] = { + { + .procname = "fips_enabled", + /* + * Note: we use fips_operational instead of fips_enabled, + * since fips_enabled is more like "FIPS was requested", + * and is nonzero before self testing and integrity testing + * has finished. However, the difference is theoretical + * since this file will not even be created until the + * testing has completed. + */ + .data = &fips_operational, + .maxlen = sizeof(int), + .mode = 0444, + .proc_handler = proc_dointvec, + }, + { + .procname = "fips_name", + .data = &fips_name, + .maxlen = sizeof(fips_name), + .mode = 0444, + .proc_handler = proc_dostring, + }, + { + .procname = "fips_version", + .data = &fips_version, + .maxlen = sizeof(fips_version), + .mode = 0444, + .proc_handler = proc_dostring, + }, + /* + * Helper file for dracut that always returns "1" to indicate + * that no userspace help is needed to get the FIPS module + * operational. + */ + { + .procname = "fips_standalone", + .data = &fips_standalone, + .maxlen = sizeof(fips_standalone), + .mode = 0444, + .proc_handler = proc_dointvec, + }, +}; + +static struct ctl_table_header *crypto_sysctls; + +static int __init check_nonfips_alg_list(void) +{ + extern struct list_head nonfips_crypto_alg_list; + + struct crypto_alg *q; + int err = 0; + + list_for_each_entry(q, &nonfips_crypto_alg_list, cra_list) { + pr_err("FIPS 140: found registered non-FIPS algorithm %s (%s)\n", q->cra_name, q->cra_driver_name); + err = 1; + } + + return err; +} + +static int __init run_initcalls(void) +{ + extern unsigned long __fips_initcalls_start[]; + extern unsigned long __fips_initcalls_end[]; + + for (unsigned long *initcall = __fips_initcalls_start; + initcall != __fips_initcalls_end; ++initcall) + { + int ret; + initcall_t fn; + + fn = (initcall_t) *initcall; + pr_info("FIPS 140: calling %pS\n", fn); + + ret = fn(); + if (!ret || ret == -ENODEV) + continue; + + panic("FIPS 140: initcall %pS failed: %d\n", fn, ret); + } + + return 0; +} + +static int __init fips140_init(void) +{ + pr_info("FIPS 140: %s version %s\n", fips_name, fips_version); + + if (check_nonfips_alg_list()) + panic("FIPS 140: found registered non-FIPS algorithms\n"); + + run_initcalls(); + + if (verify_integrity()) + panic("FIPS 140: integrity check failed\n"); + + crypto_sysctls = register_sysctl("crypto", crypto_sysctl_table); + if (!crypto_sysctls) + panic("FIPS 140: failed to register sysctls"); + + fips_operational = 1; + + pr_info("FIPS 140: operational\n"); + return 0; +} +module_init(fips140_init); + +MODULE_DESCRIPTION("FIPS 140 Cryptographic Module"); +MODULE_LICENSE("GPL"); diff --git a/fips140/fips140.lds b/fips140/fips140.lds new file mode 100644 index 000000000000..78ac3fa7a7db --- /dev/null +++ b/fips140/fips140.lds @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +SECTIONS { + .init.data : { + __fips_initcalls_start = .; + *(.fips_initcall) + __fips_initcalls_end = .; + } +} -- 2.39.3