From: Eric Biggers <ebiggers@kernel.org>
To: linux-crypto@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, Ard Biesheuvel <ardb@kernel.org>,
"Jason A . Donenfeld" <Jason@zx2c4.com>,
Herbert Xu <herbert@gondor.apana.org.au>,
Eric Biggers <ebiggers@kernel.org>
Subject: [PATCH 3/3] lib/crypto: aes: Add FIPS self-tests for GCM and CCM
Date: Sun, 2 Aug 2026 15:24:08 -0700 [thread overview]
Message-ID: <20260802222408.91757-4-ebiggers@kernel.org> (raw)
In-Reply-To: <20260802222408.91757-1-ebiggers@kernel.org>
Upcoming changes will wire up architecture-optimized implementations of
GCM and CCM. FIPS labs can consider such designs to meet the threshold
for separate self-tests to be needed.
Therefore, add FIPS self-tests for encryption and decryption in these
modes.
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
lib/crypto/aes.c | 81 +++++++++++++++++++++++++----
lib/crypto/fips-aes.h | 19 +++++++
scripts/crypto/gen-fips-testvecs.py | 23 ++++++++
3 files changed, 113 insertions(+), 10 deletions(-)
diff --git a/lib/crypto/aes.c b/lib/crypto/aes.c
index e9119f82b0cc..41aaa82cb1a1 100644
--- a/lib/crypto/aes.c
+++ b/lib/crypto/aes.c
@@ -737,14 +737,7 @@ void aes_cbcmac_final(struct aes_cbcmac_ctx *ctx, u8 out[AES_BLOCK_SIZE])
}
EXPORT_SYMBOL_NS_GPL(aes_cbcmac_final, "CRYPTO_INTERNAL");
-/*
- * FIPS cryptographic algorithm self-test for AES-CMAC. As per the FIPS 140-3
- * Implementation Guidance, a cryptographic algorithm self-test for at least one
- * of AES-GCM, AES-CCM, AES-CMAC, or AES-GMAC is required if any of those modes
- * is implemented. This fulfills that requirement via AES-CMAC.
- *
- * This is just for FIPS. The full tests are in the KUnit test suite.
- */
+/* FIPS cryptographic algorithm self-test for AES-CMAC */
static void __init aes_cmac_fips_test(void)
{
struct aes_cmac_key key;
@@ -1745,7 +1738,37 @@ int aes_gcm_decrypt(u8 *dst, const u8 *src, size_t data_len, const u8 *authtag,
}
EXPORT_SYMBOL_GPL(aes_gcm_decrypt);
-#endif /* CONFIG_CRYPTO_LIB_AES_GCM */
+/* FIPS cryptographic algorithm self-test for AES-GCM */
+static void __init aes_gcm_fips_test(void)
+{
+ const size_t data_len = sizeof(fips_test_data);
+ u8 buf[sizeof(fips_test_data) + AES_BLOCK_SIZE];
+ struct aes_gcm_key key;
+ int err;
+
+ if (aes_gcm_preparekey(&key, fips_test_key, sizeof(fips_test_key),
+ AES_BLOCK_SIZE) != 0)
+ panic("aes: GCM FIPS self-test failed (preparekey)\n");
+
+ aes_gcm_encrypt(buf, fips_test_data, data_len, &buf[data_len],
+ fips_test_ad, sizeof(fips_test_ad), fips_test_iv, &key);
+ if (memcmp(fips_test_aes_gcm_ctext_and_tag, buf, sizeof(buf)) != 0)
+ panic("aes: GCM FIPS self-test failed (wrong ciphertext and/or tag)\n");
+
+ err = aes_gcm_decrypt(buf, buf, data_len, &buf[data_len], fips_test_ad,
+ sizeof(fips_test_ad), fips_test_iv, &key);
+ if (err != 0)
+ panic("aes: GCM FIPS self-test failed (decryption failed)\n");
+ if (memcmp(fips_test_data, buf, data_len) != 0)
+ panic("aes: GCM FIPS self-test failed (wrong plaintext)\n");
+
+ memzero_explicit(&key, sizeof(key));
+}
+#else /* CONFIG_CRYPTO_LIB_AES_GCM */
+static inline void aes_gcm_fips_test(void)
+{
+}
+#endif /* !CONFIG_CRYPTO_LIB_AES_GCM */
#if IS_ENABLED(CONFIG_CRYPTO_LIB_AES_CCM)
int aes_ccm_preparekey(struct aes_ccm_key *key, const u8 *in_key,
@@ -2057,7 +2080,43 @@ int aes_ccm_decrypt(u8 *dst, const u8 *src, size_t data_len, const u8 *authtag,
return err;
}
EXPORT_SYMBOL_GPL(aes_ccm_decrypt);
-#endif /* CONFIG_CRYPTO_LIB_AES_CCM */
+
+/* FIPS cryptographic algorithm self-test for AES-CCM */
+static void __init aes_ccm_fips_test(void)
+{
+ const size_t data_len = sizeof(fips_test_data);
+ const size_t nonce_len = 13;
+ u8 buf[sizeof(fips_test_data) + AES_BLOCK_SIZE];
+ struct aes_ccm_key key;
+ int err;
+
+ if (aes_ccm_preparekey(&key, fips_test_key, sizeof(fips_test_key),
+ AES_BLOCK_SIZE) != 0)
+ panic("aes: CCM FIPS self-test failed (preparekey)\n");
+
+ err = aes_ccm_encrypt(buf, fips_test_data, data_len, &buf[data_len],
+ fips_test_ad, sizeof(fips_test_ad), fips_test_iv,
+ nonce_len, &key);
+ if (err != 0)
+ panic("aes: CCM FIPS self-test failed (encryption failed)\n");
+ if (memcmp(fips_test_aes_ccm_ctext_and_tag, buf, sizeof(buf)) != 0)
+ panic("aes: CCM FIPS self-test failed (wrong ciphertext and/or tag)\n");
+
+ err = aes_ccm_decrypt(buf, buf, data_len, &buf[data_len], fips_test_ad,
+ sizeof(fips_test_ad), fips_test_iv, nonce_len,
+ &key);
+ if (err != 0)
+ panic("aes: CCM FIPS self-test failed (decryption failed)\n");
+ if (memcmp(fips_test_data, buf, data_len) != 0)
+ panic("aes: CCM FIPS self-test failed (wrong plaintext)\n");
+
+ memzero_explicit(&key, sizeof(key));
+}
+#else /* CONFIG_CRYPTO_LIB_AES_CCM */
+static inline void aes_ccm_fips_test(void)
+{
+}
+#endif /* !CONFIG_CRYPTO_LIB_AES_CCM */
static int __init aes_mod_init(void)
{
@@ -2072,6 +2131,8 @@ static int __init aes_mod_init(void)
aes_cbc_cts_fips_test();
aes_ctr_fips_test();
aes_xts_fips_test();
+ aes_gcm_fips_test();
+ aes_ccm_fips_test();
}
return 0;
}
diff --git a/lib/crypto/fips-aes.h b/lib/crypto/fips-aes.h
index cfacf5d98e07..2a1746606533 100644
--- a/lib/crypto/fips-aes.h
+++ b/lib/crypto/fips-aes.h
@@ -9,6 +9,11 @@ static const u8 fips_test_data[] __initconst __maybe_unused = {
0x74, 0x20, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00,
};
+static const u8 fips_test_ad[] __initconst __maybe_unused = {
+ 0x66, 0x69, 0x70, 0x73, 0x20, 0x74, 0x65, 0x73,
+ 0x74, 0x20, 0x61, 0x64, 0x00, 0x00, 0x00, 0x00,
+};
+
static const u8 fips_test_iv[] __initconst __maybe_unused = {
0x66, 0x69, 0x70, 0x73, 0x20, 0x74, 0x65, 0x73,
0x74, 0x20, 0x69, 0x76, 0x00, 0x00, 0x00, 0x00,
@@ -57,3 +62,17 @@ static const u8 fips_test_aes_xts_ctext[] __initconst __maybe_unused = {
0xd4, 0x51, 0x7f, 0x01, 0x14, 0x91, 0x16, 0x29,
0x26, 0xbe, 0xec, 0x9b, 0x90, 0xed, 0x59, 0x30,
};
+
+static const u8 fips_test_aes_gcm_ctext_and_tag[] __initconst __maybe_unused = {
+ 0x12, 0x0c, 0x5d, 0x03, 0x32, 0x93, 0x13, 0x44,
+ 0x06, 0x35, 0x26, 0x9d, 0xe0, 0xea, 0xbc, 0xe2,
+ 0x30, 0xa9, 0xa4, 0x15, 0xc5, 0x3d, 0xb3, 0xf9,
+ 0x30, 0x82, 0xdf, 0x9c, 0xd8, 0xc4, 0x3f, 0x2f,
+};
+
+static const u8 fips_test_aes_ccm_ctext_and_tag[] __initconst __maybe_unused = {
+ 0x11, 0x8e, 0x01, 0xcb, 0xb5, 0x22, 0x6d, 0xb4,
+ 0x66, 0x98, 0x97, 0x1d, 0x35, 0x53, 0x78, 0xdd,
+ 0xd1, 0xc5, 0xff, 0xb6, 0x90, 0xcf, 0xb1, 0xf2,
+ 0x87, 0x99, 0xd6, 0x1e, 0xd5, 0xd1, 0xed, 0x63,
+};
diff --git a/scripts/crypto/gen-fips-testvecs.py b/scripts/crypto/gen-fips-testvecs.py
index a79eaf081c26..b8c8a78cb8a8 100755
--- a/scripts/crypto/gen-fips-testvecs.py
+++ b/scripts/crypto/gen-fips-testvecs.py
@@ -8,6 +8,7 @@
# Copyright 2025 Google LLC
import cryptography.hazmat.primitives.ciphers
+import cryptography.hazmat.primitives.ciphers.aead
import cryptography.hazmat.primitives.cmac
import hashlib
import hmac
@@ -32,12 +33,14 @@ def print_header(file):
def gen_aes_test_data(file):
fips_test_data = b"fips test data\0\0"
+ fips_test_ad = b"fips test ad\0\0\0\0"
fips_test_iv = b"fips test iv\0\0\0\0"
fips_test_key = b"fips test key\0\0\0"
fips_test_xts_key = b"key1" + (b"\0" * 12) + b"key2" + (b"\0" * 12)
print_header(file)
print_static_u8_array_definition(file, "fips_test_data", fips_test_data)
+ print_static_u8_array_definition(file, "fips_test_ad", fips_test_ad)
print_static_u8_array_definition(file, "fips_test_iv", fips_test_iv)
print_static_u8_array_definition(file, "fips_test_key", fips_test_key)
print_static_u8_array_definition(file, "fips_test_xts_key", fips_test_xts_key)
@@ -93,6 +96,26 @@ def gen_aes_test_data(file):
ctext = encryptor.update(fips_test_data) + encryptor.finalize()
print_static_u8_array_definition(file, "fips_test_aes_xts_ctext", ctext)
+ # AES-GCM
+ cipher = cryptography.hazmat.primitives.ciphers.aead.AESGCM(fips_test_key)
+ ct_and_tag = cipher.encrypt(
+ nonce=fips_test_iv[:12], data=fips_test_data, associated_data=fips_test_ad
+ )
+ print_static_u8_array_definition(
+ file, "fips_test_aes_gcm_ctext_and_tag", ct_and_tag
+ )
+
+ # AES-CCM
+ cipher = cryptography.hazmat.primitives.ciphers.aead.AESCCM(
+ fips_test_key, tag_length=16
+ )
+ ct_and_tag = cipher.encrypt(
+ nonce=fips_test_iv[:13], data=fips_test_data, associated_data=fips_test_ad
+ )
+ print_static_u8_array_definition(
+ file, "fips_test_aes_ccm_ctext_and_tag", ct_and_tag
+ )
+
def gen_sha_test_data(file):
fips_test_data = b"fips test data\0\0"
--
2.55.0
next prev parent reply other threads:[~2026-08-02 22:24 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-02 22:24 [PATCH 0/3] lib/crypto: FIPS self-tests for AES encryption modes Eric Biggers
2026-08-02 22:24 ` [PATCH 1/3] lib/crypto: fips: Split fips.h into fips-aes.h and fips-sha.h Eric Biggers
2026-08-02 22:24 ` [PATCH 2/3] lib/crypto: aes: Add FIPS self-tests for unauthenticated modes Eric Biggers
2026-08-02 22:24 ` Eric Biggers [this message]
2026-08-03 11:18 ` [PATCH 0/3] lib/crypto: FIPS self-tests for AES encryption modes Ard Biesheuvel
2026-08-05 19:46 ` Eric Biggers
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260802222408.91757-4-ebiggers@kernel.org \
--to=ebiggers@kernel.org \
--cc=Jason@zx2c4.com \
--cc=ardb@kernel.org \
--cc=herbert@gondor.apana.org.au \
--cc=linux-crypto@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox