From 60e9a0f5bec2e93c6e8fd462850676f488aa51c8 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 2 Aug 2026 15:24:06 -0700 Subject: lib/crypto: fips: Split fips.h into fips-aes.h and fips-sha.h In preparation for adding FIPS self-tests for AES encryption modes, split fips.h into separate files for the AES and SHA test vectors. They are still generated by the same script, but this keeps things a bit more organized. Reviewed-by: Ard Biesheuvel Link: https://patch.msgid.link/20260802222408.91757-2-ebiggers@kernel.org Signed-off-by: Eric Biggers --- scripts/crypto/gen-fips-testvecs.py | 93 ++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 31 deletions(-) (limited to 'scripts') diff --git a/scripts/crypto/gen-fips-testvecs.py b/scripts/crypto/gen-fips-testvecs.py index 9f18bcb97412..aa6c0a81fbf8 100755 --- a/scripts/crypto/gen-fips-testvecs.py +++ b/scripts/crypto/gen-fips-testvecs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0-or-later # -# Script that generates lib/crypto/fips.h +# Script that generates lib/crypto/fips-aes.h and lib/crypto/fips-sha.h # # Requires that python-cryptography be installed. # @@ -12,35 +12,66 @@ import cryptography.hazmat.primitives.cmac import hashlib 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 = {{') +def print_static_u8_array_definition(file, name, value): + print("", file=file) + print(f"static const u8 {name}[] __initconst __maybe_unused = {{", file=file) 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 ') - -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()) - -print_static_u8_array_definition(f'fips_test_sha3_256_value', - hashlib.sha3_256(fips_test_data).digest()) - -aes = cryptography.hazmat.primitives.ciphers.algorithms.AES(fips_test_key) -aes_cmac = cryptography.hazmat.primitives.cmac.CMAC(aes) -aes_cmac.update(fips_test_data) -print_static_u8_array_definition('fips_test_aes_cmac_value', - aes_cmac.finalize()) + line = "\t" + "".join(f"0x{b:02x}, " for b in value[i : i + 8]) + print(f"{line.rstrip()}", file=file) + print("};", file=file) + + +def print_header(file): + print("/* SPDX-License-Identifier: GPL-2.0-or-later */", file=file) + print("/* This file was generated by: gen-fips-testvecs.py */", file=file) + print("/* clang-format off */", file=file) + print("", file=file) + print("#include ", file=file) + + +def gen_aes_test_data(file): + fips_test_data = b"fips test data\0\0" + fips_test_key = b"fips test key\0\0\0" + + print_header(file) + print_static_u8_array_definition(file, "fips_test_data", fips_test_data) + print_static_u8_array_definition(file, "fips_test_key", fips_test_key) + + aes = cryptography.hazmat.primitives.ciphers.algorithms.AES(fips_test_key) + aes_cmac = cryptography.hazmat.primitives.cmac.CMAC(aes) + aes_cmac.update(fips_test_data) + print_static_u8_array_definition( + file, "fips_test_aes_cmac_value", aes_cmac.finalize() + ) + + +def gen_sha_test_data(file): + fips_test_data = b"fips test data\0\0" + fips_test_key = b"fips test key\0\0\0" + + print_header(file) + print_static_u8_array_definition(file, "fips_test_data", fips_test_data) + print_static_u8_array_definition(file, "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( + file, f"fips_test_hmac_{alg}_value", ctx.digest() + ) + + print_static_u8_array_definition( + file, "fips_test_sha3_256_value", hashlib.sha3_256(fips_test_data).digest() + ) + + +filename = "lib/crypto/fips-aes.h" +with open(filename, "w") as file: + print(f"Generating {filename}") + gen_aes_test_data(file) + +filename = "lib/crypto/fips-sha.h" +with open(filename, "w") as file: + print(f"Generating {filename}") + gen_sha_test_data(file) -- cgit v1.2.3 From e967fa98f7618e98b50b3d49a1768deba8981a98 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 2 Aug 2026 15:24:07 -0700 Subject: lib/crypto: aes: Add FIPS self-tests for unauthenticated modes Upcoming changes will wire up architecture-optimized implementations of ECB, CBC, CBC-CTS, CTR, and XTS. FIPS labs can consider such designs to meet the threshold for separate self-tests to be needed. The inverse direction of the block cipher also needs to be exercised, which the existing CMAC self-test doesn't do. Therefore, add FIPS self-tests for encryption and decryption in these modes as well as the "bare" AES. Reviewed-by: Ard Biesheuvel Link: https://patch.msgid.link/20260802222408.91757-3-ebiggers@kernel.org Signed-off-by: Eric Biggers --- scripts/crypto/gen-fips-testvecs.py | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'scripts') diff --git a/scripts/crypto/gen-fips-testvecs.py b/scripts/crypto/gen-fips-testvecs.py index aa6c0a81fbf8..a79eaf081c26 100755 --- a/scripts/crypto/gen-fips-testvecs.py +++ b/scripts/crypto/gen-fips-testvecs.py @@ -32,19 +32,67 @@ def print_header(file): def gen_aes_test_data(file): fips_test_data = b"fips test data\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_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) aes = cryptography.hazmat.primitives.ciphers.algorithms.AES(fips_test_key) + + # AES-CMAC aes_cmac = cryptography.hazmat.primitives.cmac.CMAC(aes) aes_cmac.update(fips_test_data) print_static_u8_array_definition( file, "fips_test_aes_cmac_value", aes_cmac.finalize() ) + # AES-ECB + cipher = cryptography.hazmat.primitives.ciphers.Cipher( + aes, cryptography.hazmat.primitives.ciphers.modes.ECB() + ) + encryptor = cipher.encryptor() + ctext = encryptor.update(fips_test_data) + encryptor.finalize() + print_static_u8_array_definition(file, "fips_test_aes_ecb_ctext", ctext) + + # AES-CBC + cipher = cryptography.hazmat.primitives.ciphers.Cipher( + aes, cryptography.hazmat.primitives.ciphers.modes.CBC(fips_test_iv) + ) + encryptor = cipher.encryptor() + ctext = encryptor.update(fips_test_data) + encryptor.finalize() + print_static_u8_array_definition(file, "fips_test_aes_cbc_ctext", ctext) + + # AES-CBC-CTS + cipher = cryptography.hazmat.primitives.ciphers.Cipher( + aes, cryptography.hazmat.primitives.ciphers.modes.CBC(fips_test_iv) + ) + encryptor = cipher.encryptor() + ctext = encryptor.update(fips_test_data * 2) + encryptor.finalize() + ctext = ctext[16:32] + ctext[0:16] + print_static_u8_array_definition(file, "fips_test_aes_cbc_cts_ctext", ctext) + + # AES-CTR + cipher = cryptography.hazmat.primitives.ciphers.Cipher( + aes, cryptography.hazmat.primitives.ciphers.modes.CTR(fips_test_iv) + ) + encryptor = cipher.encryptor() + ctext = encryptor.update(fips_test_data) + encryptor.finalize() + print_static_u8_array_definition(file, "fips_test_aes_ctr_ctext", ctext) + + # AES-XTS + cipher = cryptography.hazmat.primitives.ciphers.Cipher( + cryptography.hazmat.primitives.ciphers.algorithms.AES(fips_test_xts_key), + cryptography.hazmat.primitives.ciphers.modes.XTS(fips_test_iv), + ) + encryptor = cipher.encryptor() + ctext = encryptor.update(fips_test_data) + encryptor.finalize() + print_static_u8_array_definition(file, "fips_test_aes_xts_ctext", ctext) + def gen_sha_test_data(file): fips_test_data = b"fips test data\0\0" -- cgit v1.2.3 From fbdb43c0007b37e574fa0ca76afd13bed96db8f6 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 2 Aug 2026 15:24:08 -0700 Subject: 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. Reviewed-by: Ard Biesheuvel Link: https://patch.msgid.link/20260802222408.91757-4-ebiggers@kernel.org Signed-off-by: Eric Biggers --- scripts/crypto/gen-fips-testvecs.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'scripts') 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" -- cgit v1.2.3 From 2aeef50ecadca2fea0c96abed49452ff9b582b48 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 2 Aug 2026 16:30:04 -0700 Subject: lib/crypto: tests: Add KUnit test suite for AES-CCM Add a KUnit test suite for the AES-CCM library API. It consists of: - All the shared test cases from aead-test-template.h. These include extensive consistency tests, a "Monte-Carlo test", and a benchmark. - Tests against hardcoded AES-CCM test vectors from external sources. - Tests for CCM-specific message length validation. To generate the expected aes_ccm_monte_carlo_checksum[] value, add a script gen-aead-testvecs.py which computes it using python-cryptography. Reviewed-by: Ard Biesheuvel Link: https://patch.msgid.link/20260802233005.161467-5-ebiggers@kernel.org Signed-off-by: Eric Biggers --- scripts/crypto/gen-aead-testvecs.py | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100755 scripts/crypto/gen-aead-testvecs.py (limited to 'scripts') diff --git a/scripts/crypto/gen-aead-testvecs.py b/scripts/crypto/gen-aead-testvecs.py new file mode 100755 index 000000000000..048043735819 --- /dev/null +++ b/scripts/crypto/gen-aead-testvecs.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Script that generates known-good data used in the AEAD tests. +# +# Requires that python-cryptography be installed. +# +# Copyright 2026 Google LLC + +import hashlib +import sys +import cryptography.hazmat.primitives.ciphers.aead + + +# Deterministically generate 'length' random bytes. +def rand_bytes(length): + seed = length + out = [] + for _ in range(length): + seed = (seed * 25214903917 + 11) % 2**48 + out.append((seed >> 16) % 256) + return bytes(out) + + +# Deterministically generate many different AEAD inputs using exactly the same +# method that the test uses; encrypt them using an independent implementation of +# the algorithm; compute the checksum of all the resulting (ciphertext, authtag) +# pairs concatenated to each other; and print the checksum as a C struct. +def gen_monte_carlo_checksum(alg): + blake2s = hashlib.blake2s() + for data_len in range(1025): + ad_len = data_len % 293 + pt = rand_bytes(data_len) + ad = rand_bytes(ad_len) + if alg == "aes-ccm": + key_len = [16, 24, 32][data_len % 3] + key = rand_bytes(key_len) + nonce = rand_bytes([7, 8, 9, 10, 11, 12, 13][data_len % 7]) + tag_len = [4, 6, 8, 10, 12, 14, 16][data_len % 7] + ccm = cryptography.hazmat.primitives.ciphers.aead.AESCCM( + key, tag_length=tag_len + ) + ct_and_tag = ccm.encrypt(nonce, pt, ad) + + blake2s.update(ct_and_tag) + + name = f"{alg.replace('-', '_')}_monte_carlo_checksum" + value = blake2s.digest() + print(f"static const u8 {name}[BLAKE2S_HASH_SIZE] = {{") + for i in range(0, len(value), 11): + line = "\t" + "".join(f"0x{b:02x}, " for b in value[i : i + 11]) + print(f"{line.rstrip()}") + print("};") + + +if len(sys.argv) != 2 or sys.argv[1] not in ("aes-ccm"): + sys.stderr.write("Usage: gen-aead-testvecs.py [aes-ccm]\n") + sys.exit(1) + +gen_monte_carlo_checksum(sys.argv[1]) -- cgit v1.2.3 From b09bd2d92ee1bbbf6db4533d5d9d2a7b39ae48c4 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 2 Aug 2026 16:30:05 -0700 Subject: lib/crypto: tests: Add KUnit test suite for AES-GCM Add a KUnit test suite for the AES-GCM library API. It consists of: - All the shared test cases from aead-test-template.h. These include extensive consistency tests, a "Monte-Carlo test", and a benchmark. - Tests against hardcoded AES-GCM test vectors from external sources. Reviewed-by: Ard Biesheuvel Link: https://patch.msgid.link/20260802233005.161467-6-ebiggers@kernel.org Signed-off-by: Eric Biggers --- scripts/crypto/gen-aead-testvecs.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/crypto/gen-aead-testvecs.py b/scripts/crypto/gen-aead-testvecs.py index 048043735819..d77d646f75b5 100755 --- a/scripts/crypto/gen-aead-testvecs.py +++ b/scripts/crypto/gen-aead-testvecs.py @@ -41,6 +41,15 @@ def gen_monte_carlo_checksum(alg): key, tag_length=tag_len ) ct_and_tag = ccm.encrypt(nonce, pt, ad) + elif alg == "aes-gcm": + key_len = [16, 24, 32][data_len % 3] + key = rand_bytes(key_len) + nonce = rand_bytes(12) + tag_len = [4, 8, 12, 13, 14, 15, 16][data_len % 7] + gcm = cryptography.hazmat.primitives.ciphers.aead.AESGCM(key) + # python-cryptography supports only 16-byte GCM tags. However, in + # GCM, shorter tags are simply truncated. Do that below. + ct_and_tag = gcm.encrypt(nonce, pt, ad)[: data_len + tag_len] blake2s.update(ct_and_tag) @@ -53,8 +62,8 @@ def gen_monte_carlo_checksum(alg): print("};") -if len(sys.argv) != 2 or sys.argv[1] not in ("aes-ccm"): - sys.stderr.write("Usage: gen-aead-testvecs.py [aes-ccm]\n") +if len(sys.argv) != 2 or sys.argv[1] not in ("aes-ccm", "aes-gcm"): + sys.stderr.write("Usage: gen-aead-testvecs.py [aes-ccm|aes-gcm]\n") sys.exit(1) gen_monte_carlo_checksum(sys.argv[1]) -- cgit v1.2.3