History

lib/crypto: aes: Add FIPS self-tests for GCM and CCM

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.

Eric Biggers committed 3 months ago

Commit fbdb43c1 parent e967fa93 files · +113 −10Browse files

Unified · Split

3 files
  1. lib/crypto/aes.c +71 −10
  2. lib/crypto/fips-aes.h +19 −0
  3. scripts/crypto/gen-fips-testvecs.py +23 −0
lib/crypto/aes.c+71−10
@@ -737,14 +737,7 @@ void aes_cbcmac_final(struct aes_cbcmac_ctx *ctx, u8 out[AES_BLOCK_SIZE]) · 736 unmodified lines
737737 }
738738 EXPORT_SYMBOL_NS_GPL(aes_cbcmac_final, "CRYPTO_INTERNAL");
739739
740−/*
741− * FIPS cryptographic algorithm self-test for AES-CMAC. As per the FIPS 140-3
742− * Implementation Guidance, a cryptographic algorithm self-test for at least one
743− * of AES-GCM, AES-CCM, AES-CMAC, or AES-GMAC is required if any of those modes
744− * is implemented. This fulfills that requirement via AES-CMAC.
745− *
746− * This is just for FIPS. The full tests are in the KUnit test suite.
747− */
740+/* FIPS cryptographic algorithm self-test for AES-CMAC */
748741 static void __init aes_cmac_fips_test(void)
749742 {
750743 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, · 994 unmodified lines
17451738 }
17461739 EXPORT_SYMBOL_GPL(aes_gcm_decrypt);
17471740
1748−#endif /* CONFIG_CRYPTO_LIB_AES_GCM */
1741+/* FIPS cryptographic algorithm self-test for AES-GCM */
1742+static void __init aes_gcm_fips_test(void)
1743+{
1744+ const size_t data_len = sizeof(fips_test_data);
1745+ u8 buf[sizeof(fips_test_data) + AES_BLOCK_SIZE];
1746+ struct aes_gcm_key key;
1747+ int err;
1748+
1749+ if (aes_gcm_preparekey(&key, fips_test_key, sizeof(fips_test_key),
1750+ AES_BLOCK_SIZE) != 0)
1751+ panic("aes: GCM FIPS self-test failed (preparekey)\n");
1752+
1753+ aes_gcm_encrypt(buf, fips_test_data, data_len, &buf[data_len],
1754+ fips_test_ad, sizeof(fips_test_ad), fips_test_iv, &key);
1755+ if (memcmp(fips_test_aes_gcm_ctext_and_tag, buf, sizeof(buf)) != 0)
1756+ panic("aes: GCM FIPS self-test failed (wrong ciphertext and/or tag)\n");
1757+
1758+ err = aes_gcm_decrypt(buf, buf, data_len, &buf[data_len], fips_test_ad,
1759+ sizeof(fips_test_ad), fips_test_iv, &key);
1760+ if (err != 0)
1761+ panic("aes: GCM FIPS self-test failed (decryption failed)\n");
1762+ if (memcmp(fips_test_data, buf, data_len) != 0)
1763+ panic("aes: GCM FIPS self-test failed (wrong plaintext)\n");
1764+
1765+ memzero_explicit(&key, sizeof(key));
1766+}
1767+#else /* CONFIG_CRYPTO_LIB_AES_GCM */
1768+static inline void aes_gcm_fips_test(void)
1769+{
1770+}
1771+#endif /* !CONFIG_CRYPTO_LIB_AES_GCM */
17491772
17501773 #if IS_ENABLED(CONFIG_CRYPTO_LIB_AES_CCM)
17511774 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, · 305 unmodified lines
20572080 return err;
20582081 }
20592082 EXPORT_SYMBOL_GPL(aes_ccm_decrypt);
2060−#endif /* CONFIG_CRYPTO_LIB_AES_CCM */
2083+
2084+/* FIPS cryptographic algorithm self-test for AES-CCM */
2085+static void __init aes_ccm_fips_test(void)
2086+{
2087+ const size_t data_len = sizeof(fips_test_data);
2088+ const size_t nonce_len = 13;
2089+ u8 buf[sizeof(fips_test_data) + AES_BLOCK_SIZE];
2090+ struct aes_ccm_key key;
2091+ int err;
2092+
2093+ if (aes_ccm_preparekey(&key, fips_test_key, sizeof(fips_test_key),
2094+ AES_BLOCK_SIZE) != 0)
2095+ panic("aes: CCM FIPS self-test failed (preparekey)\n");
2096+
2097+ err = aes_ccm_encrypt(buf, fips_test_data, data_len, &buf[data_len],
2098+ fips_test_ad, sizeof(fips_test_ad), fips_test_iv,
2099+ nonce_len, &key);
2100+ if (err != 0)
2101+ panic("aes: CCM FIPS self-test failed (encryption failed)\n");
2102+ if (memcmp(fips_test_aes_ccm_ctext_and_tag, buf, sizeof(buf)) != 0)
2103+ panic("aes: CCM FIPS self-test failed (wrong ciphertext and/or tag)\n");
2104+
2105+ err = aes_ccm_decrypt(buf, buf, data_len, &buf[data_len], fips_test_ad,
2106+ sizeof(fips_test_ad), fips_test_iv, nonce_len,
2107+ &key);
2108+ if (err != 0)
2109+ panic("aes: CCM FIPS self-test failed (decryption failed)\n");
2110+ if (memcmp(fips_test_data, buf, data_len) != 0)
2111+ panic("aes: CCM FIPS self-test failed (wrong plaintext)\n");
2112+
2113+ memzero_explicit(&key, sizeof(key));
2114+}
2115+#else /* CONFIG_CRYPTO_LIB_AES_CCM */
2116+static inline void aes_ccm_fips_test(void)
2117+{
2118+}
2119+#endif /* !CONFIG_CRYPTO_LIB_AES_CCM */
20612120
20622121 static int __init aes_mod_init(void)
20632122 {
@@ -2072,6 +2131,8 @@ static int __init aes_mod_init(void) · 8 unmodified lines
20722131 aes_cbc_cts_fips_test();
20732132 aes_ctr_fips_test();
20742133 aes_xts_fips_test();
2134+ aes_gcm_fips_test();
2135+ aes_ccm_fips_test();
20752136 }
20762137 return 0;
20772138 }
lib/crypto/fips-aes.h+19−0
@@ -9,6 +9,11 @@ static const u8 fips_test_data[] __initconst __maybe_unused = { · 8 unmodified lines
99 0x74, 0x20, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00,
1010 };
1111
12+static const u8 fips_test_ad[] __initconst __maybe_unused = {
13+ 0x66, 0x69, 0x70, 0x73, 0x20, 0x74, 0x65, 0x73,
14+ 0x74, 0x20, 0x61, 0x64, 0x00, 0x00, 0x00, 0x00,
15+};
16+
1217 static const u8 fips_test_iv[] __initconst __maybe_unused = {
1318 0x66, 0x69, 0x70, 0x73, 0x20, 0x74, 0x65, 0x73,
1419 0x74, 0x20, 0x69, 0x76, 0x00, 0x00, 0x00, 0x00,
@@ -57,3 +62,17 @@ static const u8 fips_test_aes_xts_ctext[] __initconst __maybe_unused = { · 42 unmodified lines
5762 0xd4, 0x51, 0x7f, 0x01, 0x14, 0x91, 0x16, 0x29,
5863 0x26, 0xbe, 0xec, 0x9b, 0x90, 0xed, 0x59, 0x30,
5964 };
65+
66+static const u8 fips_test_aes_gcm_ctext_and_tag[] __initconst __maybe_unused = {
67+ 0x12, 0x0c, 0x5d, 0x03, 0x32, 0x93, 0x13, 0x44,
68+ 0x06, 0x35, 0x26, 0x9d, 0xe0, 0xea, 0xbc, 0xe2,
69+ 0x30, 0xa9, 0xa4, 0x15, 0xc5, 0x3d, 0xb3, 0xf9,
70+ 0x30, 0x82, 0xdf, 0x9c, 0xd8, 0xc4, 0x3f, 0x2f,
71+};
72+
73+static const u8 fips_test_aes_ccm_ctext_and_tag[] __initconst __maybe_unused = {
74+ 0x11, 0x8e, 0x01, 0xcb, 0xb5, 0x22, 0x6d, 0xb4,
75+ 0x66, 0x98, 0x97, 0x1d, 0x35, 0x53, 0x78, 0xdd,
76+ 0xd1, 0xc5, 0xff, 0xb6, 0x90, 0xcf, 0xb1, 0xf2,
77+ 0x87, 0x99, 0xd6, 0x1e, 0xd5, 0xd1, 0xed, 0x63,
78+};
scripts/crypto/gen-fips-testvecs.py+23−0
@@ -8,6 +8,7 @@ · 7 unmodified lines
88 # Copyright 2025 Google LLC
99
1010 import cryptography.hazmat.primitives.ciphers
11+import cryptography.hazmat.primitives.ciphers.aead
1112 import cryptography.hazmat.primitives.cmac
1213 import hashlib
1314 import hmac
@@ -32,12 +33,14 @@ def print_header(file): · 18 unmodified lines
3233
3334 def gen_aes_test_data(file):
3435 fips_test_data = b"fips test data\0\0"
36+ fips_test_ad = b"fips test ad\0\0\0\0"
3537 fips_test_iv = b"fips test iv\0\0\0\0"
3638 fips_test_key = b"fips test key\0\0\0"
3739 fips_test_xts_key = b"key1" + (b"\0" * 12) + b"key2" + (b"\0" * 12)
3840
3941 print_header(file)
4042 print_static_u8_array_definition(file, "fips_test_data", fips_test_data)
43+ print_static_u8_array_definition(file, "fips_test_ad", fips_test_ad)
4144 print_static_u8_array_definition(file, "fips_test_iv", fips_test_iv)
4245 print_static_u8_array_definition(file, "fips_test_key", fips_test_key)
4346 print_static_u8_array_definition(file, "fips_test_xts_key", fips_test_xts_key)
@@ -93,6 +96,26 @@ def gen_aes_test_data(file): · 49 unmodified lines
9396 ctext = encryptor.update(fips_test_data) + encryptor.finalize()
9497 print_static_u8_array_definition(file, "fips_test_aes_xts_ctext", ctext)
9598
99+ # AES-GCM
100+ cipher = cryptography.hazmat.primitives.ciphers.aead.AESGCM(fips_test_key)
101+ ct_and_tag = cipher.encrypt(
102+ nonce=fips_test_iv[:12], data=fips_test_data, associated_data=fips_test_ad
103+ )
104+ print_static_u8_array_definition(
105+ file, "fips_test_aes_gcm_ctext_and_tag", ct_and_tag
106+ )
107+
108+ # AES-CCM
109+ cipher = cryptography.hazmat.primitives.ciphers.aead.AESCCM(
110+ fips_test_key, tag_length=16
111+ )
112+ ct_and_tag = cipher.encrypt(
113+ nonce=fips_test_iv[:13], data=fips_test_data, associated_data=fips_test_ad
114+ )
115+ print_static_u8_array_definition(
116+ file, "fips_test_aes_ccm_ctext_and_tag", ct_and_tag
117+ )
118+
96119
97120 def gen_sha_test_data(file):
98121 fips_test_data = b"fips test data\0\0"