]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
lib/crypto: Add FIPS self-tests for SHA-1 and SHA-2
authorEric Biggers <ebiggers@kernel.org>
Sat, 11 Oct 2025 00:10:47 +0000 (17:10 -0700)
committerEric Biggers <ebiggers@kernel.org>
Thu, 30 Oct 2025 05:04:24 +0000 (22:04 -0700)
Add FIPS cryptographic algorithm self-tests for all SHA-1 and SHA-2
algorithms.  Following the "Implementation Guidance for FIPS 140-3"
document, to achieve this it's sufficient to just test a single test
vector for each of HMAC-SHA1, HMAC-SHA256, and HMAC-SHA512.

Just run these tests in the initcalls, following the example of e.g.
crypto/kdf_sp800108.c.  Note that this should meet the FIPS self-test
requirement even in the built-in case, given that the initcalls run
before userspace, storage, network, etc. are accessible.

This does not fix a regression, seeing as lib/ has had SHA-1 support
since 2005 and SHA-256 support since 2018.  Neither ever had FIPS
self-tests.  Moreover, fips=1 support has always been an unfinished
feature upstream.  However, with lib/ now being used more widely, it's
now seeing more scrutiny and people seem to want these now [1][2].

[1] https://lore.kernel.org/r/3226361.1758126043@warthog.procyon.org.uk/
[2] https://lore.kernel.org/r/f31dbb22-0add-481c-aee0-e337a7731f8e@oracle.com/

Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20251011001047.51886-1-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
lib/crypto/fips.h [new file with mode: 0644]
lib/crypto/sha1.c
lib/crypto/sha256.c
lib/crypto/sha512.c
scripts/crypto/gen-fips-testvecs.py [new file with mode: 0755]

diff --git a/lib/crypto/fips.h b/lib/crypto/fips.h
new file mode 100644 (file)
index 0000000..78a1bdd
--- /dev/null
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* This file was generated by: gen-fips-testvecs.py */
+
+#include <linux/fips.h>
+
+static const u8 fips_test_data[] __initconst __maybe_unused = {
+       0x66, 0x69, 0x70, 0x73, 0x20, 0x74, 0x65, 0x73,
+       0x74, 0x20, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00,
+};
+
+static const u8 fips_test_key[] __initconst __maybe_unused = {
+       0x66, 0x69, 0x70, 0x73, 0x20, 0x74, 0x65, 0x73,
+       0x74, 0x20, 0x6b, 0x65, 0x79, 0x00, 0x00, 0x00,
+};
+
+static const u8 fips_test_hmac_sha1_value[] __initconst __maybe_unused = {
+       0x29, 0xa9, 0x88, 0xb8, 0x5c, 0xb4, 0xaf, 0x4b,
+       0x97, 0x2a, 0xee, 0x87, 0x5b, 0x0a, 0x02, 0x55,
+       0x99, 0xbf, 0x86, 0x78,
+};
+
+static const u8 fips_test_hmac_sha256_value[] __initconst __maybe_unused = {
+       0x59, 0x25, 0x85, 0xcc, 0x40, 0xe9, 0x64, 0x2f,
+       0xe9, 0xbf, 0x82, 0xb7, 0xd3, 0x15, 0x3d, 0x43,
+       0x22, 0x0b, 0x4c, 0x00, 0x90, 0x14, 0x25, 0xcf,
+       0x9e, 0x13, 0x2b, 0xc2, 0x30, 0xe6, 0xe8, 0x93,
+};
+
+static const u8 fips_test_hmac_sha512_value[] __initconst __maybe_unused = {
+       0x6b, 0xea, 0x5d, 0x27, 0x49, 0x5b, 0x3f, 0xea,
+       0xde, 0x2d, 0xfa, 0x32, 0x75, 0xdb, 0x77, 0xc8,
+       0x26, 0xe9, 0x4e, 0x95, 0x4d, 0xad, 0x88, 0x02,
+       0x87, 0xf9, 0x52, 0x0a, 0xd1, 0x92, 0x80, 0x1d,
+       0x92, 0x7e, 0x3c, 0xbd, 0xb1, 0x3c, 0x49, 0x98,
+       0x44, 0x9c, 0x8f, 0xee, 0x3f, 0x02, 0x71, 0x51,
+       0x57, 0x0b, 0x15, 0x38, 0x95, 0xd8, 0xa3, 0x81,
+       0xba, 0xb3, 0x15, 0x37, 0x5c, 0x6d, 0x57, 0x2b,
+};
index 5904e4ae85d2407f022aba83c7e1f4ce007ee40d..52788278cd17f1e00af509f7ddccd58cfd791e81 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/string.h>
 #include <linux/unaligned.h>
 #include <linux/wordpart.h>
+#include "fips.h"
 
 static const struct sha1_block_state sha1_iv = {
        .h = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 },
@@ -330,10 +331,26 @@ void hmac_sha1_usingrawkey(const u8 *raw_key, size_t raw_key_len,
 }
 EXPORT_SYMBOL_GPL(hmac_sha1_usingrawkey);
 
-#ifdef sha1_mod_init_arch
+#if defined(sha1_mod_init_arch) || defined(CONFIG_CRYPTO_FIPS)
 static int __init sha1_mod_init(void)
 {
+#ifdef sha1_mod_init_arch
        sha1_mod_init_arch();
+#endif
+       if (fips_enabled) {
+               /*
+                * FIPS cryptographic algorithm self-test.  As per the FIPS
+                * Implementation Guidance, testing HMAC-SHA1 satisfies the test
+                * requirement for SHA-1 too.
+                */
+               u8 mac[SHA1_DIGEST_SIZE];
+
+               hmac_sha1_usingrawkey(fips_test_key, sizeof(fips_test_key),
+                                     fips_test_data, sizeof(fips_test_data),
+                                     mac);
+               if (memcmp(fips_test_hmac_sha1_value, mac, sizeof(mac)) != 0)
+                       panic("sha1: FIPS self-test failed\n");
+       }
        return 0;
 }
 subsys_initcall(sha1_mod_init);
index 881b935418cead822a7695ba20848da325922ba0..5d6b77e7e14169a1682d1cf5d6927be569143554 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/string.h>
 #include <linux/unaligned.h>
 #include <linux/wordpart.h>
+#include "fips.h"
 
 static const struct sha256_block_state sha224_iv = {
        .h = {
@@ -269,8 +270,8 @@ void sha256(const u8 *data, size_t len, u8 out[SHA256_DIGEST_SIZE])
 EXPORT_SYMBOL(sha256);
 
 /*
- * Pre-boot environment (as indicated by __DISABLE_EXPORTS being defined)
- * doesn't need either HMAC support or interleaved hashing support
+ * Pre-boot environments (as indicated by __DISABLE_EXPORTS being defined) just
+ * need the generic SHA-256 code.  Omit all other features from them.
  */
 #ifndef __DISABLE_EXPORTS
 
@@ -477,12 +478,27 @@ void hmac_sha256_usingrawkey(const u8 *raw_key, size_t raw_key_len,
        hmac_sha256_final(&ctx, out);
 }
 EXPORT_SYMBOL_GPL(hmac_sha256_usingrawkey);
-#endif /* !__DISABLE_EXPORTS */
 
-#ifdef sha256_mod_init_arch
+#if defined(sha256_mod_init_arch) || defined(CONFIG_CRYPTO_FIPS)
 static int __init sha256_mod_init(void)
 {
+#ifdef sha256_mod_init_arch
        sha256_mod_init_arch();
+#endif
+       if (fips_enabled) {
+               /*
+                * FIPS cryptographic algorithm self-test.  As per the FIPS
+                * Implementation Guidance, testing HMAC-SHA256 satisfies the
+                * test requirement for SHA-224, SHA-256, and HMAC-SHA224 too.
+                */
+               u8 mac[SHA256_DIGEST_SIZE];
+
+               hmac_sha256_usingrawkey(fips_test_key, sizeof(fips_test_key),
+                                       fips_test_data, sizeof(fips_test_data),
+                                       mac);
+               if (memcmp(fips_test_hmac_sha256_value, mac, sizeof(mac)) != 0)
+                       panic("sha256: FIPS self-test failed\n");
+       }
        return 0;
 }
 subsys_initcall(sha256_mod_init);
@@ -493,5 +509,7 @@ static void __exit sha256_mod_exit(void)
 module_exit(sha256_mod_exit);
 #endif
 
+#endif /* !__DISABLE_EXPORTS */
+
 MODULE_DESCRIPTION("SHA-224, SHA-256, HMAC-SHA224, and HMAC-SHA256 library functions");
 MODULE_LICENSE("GPL");
index d8062188be98a7f8a26827f7e695090e086f8034..605eab51aabd5a991c75ef9c5106192b7a6e8d60 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/string.h>
 #include <linux/unaligned.h>
 #include <linux/wordpart.h>
+#include "fips.h"
 
 static const struct sha512_block_state sha384_iv = {
        .h = {
@@ -405,10 +406,26 @@ void hmac_sha512_usingrawkey(const u8 *raw_key, size_t raw_key_len,
 }
 EXPORT_SYMBOL_GPL(hmac_sha512_usingrawkey);
 
-#ifdef sha512_mod_init_arch
+#if defined(sha512_mod_init_arch) || defined(CONFIG_CRYPTO_FIPS)
 static int __init sha512_mod_init(void)
 {
+#ifdef sha512_mod_init_arch
        sha512_mod_init_arch();
+#endif
+       if (fips_enabled) {
+               /*
+                * FIPS cryptographic algorithm self-test.  As per the FIPS
+                * Implementation Guidance, testing HMAC-SHA512 satisfies the
+                * test requirement for SHA-384, SHA-512, and HMAC-SHA384 too.
+                */
+               u8 mac[SHA512_DIGEST_SIZE];
+
+               hmac_sha512_usingrawkey(fips_test_key, sizeof(fips_test_key),
+                                       fips_test_data, sizeof(fips_test_data),
+                                       mac);
+               if (memcmp(fips_test_hmac_sha512_value, mac, sizeof(mac)) != 0)
+                       panic("sha512: FIPS self-test failed\n");
+       }
        return 0;
 }
 subsys_initcall(sha512_mod_init);
diff --git a/scripts/crypto/gen-fips-testvecs.py b/scripts/crypto/gen-fips-testvecs.py
new file mode 100755 (executable)
index 0000000..2956f88
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Script that generates lib/crypto/fips.h
+#
+# Copyright 2025 Google LLC
+
+import hmac
+
+fips_test_data = b"fips test data\0\0"
+fips_test_key = b"fips test key\0\0\0"
+
+def print_static_u8_array_definition(name, value):
+    print('')
+    print(f'static const u8 {name}[] __initconst __maybe_unused = {{')
+    for i in range(0, len(value), 8):
+        line = '\t' + ''.join(f'0x{b:02x}, ' for b in value[i:i+8])
+        print(f'{line.rstrip()}')
+    print('};')
+
+print('/* SPDX-License-Identifier: GPL-2.0-or-later */')
+print(f'/* This file was generated by: gen-fips-testvecs.py */')
+print()
+print('#include <linux/fips.h>')
+
+print_static_u8_array_definition("fips_test_data", fips_test_data)
+print_static_u8_array_definition("fips_test_key", fips_test_key)
+
+for alg in 'sha1', 'sha256', 'sha512':
+    ctx = hmac.new(fips_test_key, digestmod=alg)
+    ctx.update(fips_test_data)
+    print_static_u8_array_definition(f'fips_test_hmac_{alg}_value', ctx.digest())