You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
ANDROID: fips140: add power-up cryptographic self-tests
Make fips140.ko run a suite of known answer self-tests at load time to demonstrate the correct operation of cryptographic functionality, as required by FIPS 140-2/3 and NIAP FPT_TST_EXT.1.1. Bug: 153614920 Bug: 173104584 Bug: 188620248 Test: Built and loaded fips140.ko on a HiKey960, and on a Pixel device. Change-Id: I38e5c8052ff57ddfe44624beb626d38b7706b0a4 Co-developed-by: Elena Petrova <lenaptr@google.com> Signed-off-by: Elena Petrova <lenaptr@google.com> [ebiggers: Rewrote most of lenaptr@'s original patch. Added some missing tests, removed some unnecessary tests in accordance with the FIPS 140-2 IG, changed most test vectors and added a script to generate them, removed an unnecessary kconfig option, changed implementation of error injection, and many other improvements.] Signed-off-by: Eric Biggers <ebiggers@google.com> [ardb: add generation of AES-CTR test vector and the associated runtime selftest] Signed-off-by: Ard Biesheuvel <ardb@google.com>
This commit is contained in:
committed by
Ard Biesheuvel
parent
bd7d13c36e
commit
b7397e89db
@@ -40,6 +40,15 @@ config CRYPTO_FIPS140_MOD
|
||||
bool "Enable FIPS140 integrity self-checked loadable module"
|
||||
depends on LTO_CLANG && CRYPTO_FIPS140
|
||||
|
||||
config CRYPTO_FIPS140_MOD_ERROR_INJECTION
|
||||
bool "Support injecting failures into the FIPS 140 self-tests"
|
||||
depends on CRYPTO_FIPS140_MOD
|
||||
help
|
||||
This option adds a module parameter "broken_alg" to the fips140 module
|
||||
which can be used to fail the self-tests for a particular algorithm,
|
||||
causing a kernel panic. This option is for FIPS lab testing only, and
|
||||
it shouldn't be enabled on production systems.
|
||||
|
||||
config CRYPTO_ALGAPI
|
||||
tristate
|
||||
select CRYPTO_ALGAPI2
|
||||
|
||||
@@ -228,7 +228,7 @@ $(obj)/lib-crypto-%-fips.o: $(srctree)/lib/crypto/%.c FORCE
|
||||
$(obj)/crypto-fips.a: $(addprefix $(obj)/,$(crypto-fips-objs)) FORCE
|
||||
$(call if_changed,ar_and_symver)
|
||||
|
||||
fips140-objs := fips140-module.o crypto-fips.a
|
||||
fips140-objs := fips140-module.o fips140-selftests.o crypto-fips.a
|
||||
obj-m += fips140.o
|
||||
|
||||
CFLAGS_fips140-module.o += $(FIPS140_CFLAGS)
|
||||
|
||||
65
crypto/fips140-generated-testvecs.h
Normal file
65
crypto/fips140-generated-testvecs.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright 2021 Google LLC */
|
||||
|
||||
/*
|
||||
* This header was automatically generated by gen_fips140_testvecs.py.
|
||||
* Don't edit it directly.
|
||||
*/
|
||||
|
||||
static const u8 fips_message[32] __initconst =
|
||||
"This is a 32-byte test message.";
|
||||
|
||||
static const u8 fips_aes_key[16] __initconst = "128-bit AES key";
|
||||
|
||||
static const u8 fips_aes_iv[16] __initconst = "ABCDEFGHIJKL";
|
||||
|
||||
static const u8 fips_aes_cbc_ciphertext[32] __initconst =
|
||||
"\xc4\x6d\xad\xa4\x04\x52\x11\x5a\x7a\xb3\x7c\x68\x85\x8d\x90\xf0"
|
||||
"\x55\xc3\xd3\x35\xc1\x75\x31\x90\xdf\x90\x4b\x5a\x56\xfd\xa7\x89";
|
||||
|
||||
static const u8 fips_aes_ecb_ciphertext[32] __initconst =
|
||||
"\xc1\x9d\xe6\xb8\xb2\x90\xff\xfe\xf2\x77\x18\xb0\x55\xd3\xee\xa9"
|
||||
"\xe2\x6f\x4a\x32\x67\xfd\xb7\xa5\x2f\x4b\x6e\x1a\x86\x2b\x6e\x3a";
|
||||
|
||||
static const u8 fips_aes_ctr_ciphertext[32] __initconst =
|
||||
"\x92\xbe\x23\xa1\x80\x88\x5d\x31\x27\xb3\x9c\x40\x58\x57\x1d\xde"
|
||||
"\xc1\x8d\x5b\xe7\x42\x93\x09\xf8\xd4\xf7\x49\x42\xcf\x40\x62\x7e";
|
||||
|
||||
static const u8 fips_aes_gcm_assoc[22] __initconst = "associated data string";
|
||||
|
||||
static const u8 fips_aes_gcm_ciphertext[48] __initconst =
|
||||
"\x37\x88\x3e\x1d\x58\x50\xda\x10\x07\xeb\x52\xdf\xea\x0a\x54\xd4"
|
||||
"\x44\xbf\x88\x2a\xf3\x03\x03\x84\xaf\x8b\x96\xbd\xea\x65\x60\x6f"
|
||||
"\x82\xfa\x51\xf4\x28\xad\x0c\xf1\xce\x0f\x91\xdd\x1a\x4c\x77\x5f";
|
||||
|
||||
static const u8 fips_aes_xts_key[32] __initconst =
|
||||
"This is an AES-128-XTS key.";
|
||||
|
||||
static const u8 fips_aes_xts_ciphertext[32] __initconst =
|
||||
"\x5e\xb9\x98\xd6\x26\xb3\x55\xbf\x44\xab\x3e\xae\x73\xc0\x81\xc9"
|
||||
"\xf4\x29\x0e\x17\x1e\xc5\xc8\x90\x79\x99\xf1\x43\x3a\x23\x08\x5a";
|
||||
|
||||
static const u8 fips_hmac_key[16] __initconst = "128-bit HMAC key";
|
||||
|
||||
static const u8 fips_sha1_digest[20] __initconst =
|
||||
"\x1b\x78\xc7\x4b\xd5\xd4\x83\xb1\x58\xc5\x96\x83\x4f\x16\x8d\x15"
|
||||
"\xb4\xaa\x22\x8c";
|
||||
|
||||
static const u8 fips_sha256_digest[32] __initconst =
|
||||
"\x4e\x11\x83\x0c\x53\x80\x1e\x5f\x9b\x38\x33\x38\xe8\x74\x43\xb0"
|
||||
"\xc1\x3a\xbe\xbf\x75\xf0\x12\x0f\x21\x33\xf5\x16\x33\xf1\xb0\x81";
|
||||
|
||||
static const u8 fips_hmac_sha256_digest[32] __initconst =
|
||||
"\x63\x0e\xb5\x73\x79\xfc\xaf\x5f\x86\xe3\xaf\xf0\xc8\x36\xef\xd5"
|
||||
"\x35\x8d\x40\x25\x38\xb3\x65\x72\x98\xf3\x59\xd8\x1e\x54\x4c\xa1";
|
||||
|
||||
static const u8 fips_sha512_digest[64] __initconst =
|
||||
"\x32\xe0\x44\x23\xbd\xe3\xec\x28\xbf\xf1\x34\x11\xd5\xae\xbf\xd5"
|
||||
"\xc0\x8e\xb5\xa1\x04\xef\x2f\x07\x84\xf1\xd9\x83\x0f\x6c\x31\xab"
|
||||
"\xf7\xe7\x57\xfa\xf7\xae\xf0\x6f\xb2\x16\x08\x32\xcf\xc7\xef\x35"
|
||||
"\xb3\x3b\x51\xb9\xfd\xe7\xff\x5e\xb2\x8b\xc6\x79\xe6\x14\x04\xb4";
|
||||
|
||||
/*
|
||||
* This header was automatically generated by gen_fips140_testvecs.py.
|
||||
* Don't edit it directly.
|
||||
*/
|
||||
@@ -11,8 +11,6 @@
|
||||
* a cryptographic software module.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "fips140: " fmt
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/module.h>
|
||||
#include <crypto/aead.h>
|
||||
@@ -23,8 +21,18 @@
|
||||
#include <crypto/rng.h>
|
||||
#include <trace/hooks/fips140.h>
|
||||
|
||||
#include "fips140-module.h"
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* This option allows deliberately failing the self-tests for a particular
|
||||
* algorithm. This is for FIPS lab testing only.
|
||||
*/
|
||||
#ifdef CONFIG_CRYPTO_FIPS140_MOD_ERROR_INJECTION
|
||||
char *fips140_broken_alg;
|
||||
module_param_named(broken_alg, fips140_broken_alg, charp, 0);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* FIPS 140-2 prefers the use of HMAC with a public key over a plain hash.
|
||||
*/
|
||||
@@ -52,6 +60,12 @@ const u32 *__initcall_start = &__initcall_start_marker;
|
||||
const u8 *__text_start = &__fips140_text_start;
|
||||
const u8 *__rodata_start = &__fips140_rodata_start;
|
||||
|
||||
/*
|
||||
* The list of the crypto API algorithms (by cra_name) that will be unregistered
|
||||
* by this module, in preparation for the module registering its own
|
||||
* implementation(s) of them. When adding a new algorithm here, make sure to
|
||||
* consider whether it needs a self-test added to fips140_selftests[] as well.
|
||||
*/
|
||||
static const char * const fips140_algorithms[] __initconst = {
|
||||
"aes",
|
||||
|
||||
@@ -566,13 +580,16 @@ fips140_init(void)
|
||||
*/
|
||||
synchronize_rcu_tasks();
|
||||
|
||||
/* insert self tests here */
|
||||
if (!fips140_run_selftests())
|
||||
goto panic;
|
||||
|
||||
/*
|
||||
* It may seem backward to perform the integrity check last, but this
|
||||
* is intentional: the check itself uses hmac(sha256) which is one of
|
||||
* the algorithms that are replaced with versions from this module, and
|
||||
* the integrity check must use the replacement version.
|
||||
* the integrity check must use the replacement version. Also, to be
|
||||
* ready for FIPS 140-3, the integrity check algorithm must have already
|
||||
* been self-tested.
|
||||
*/
|
||||
|
||||
if (!check_fips140_module_hmac()) {
|
||||
|
||||
20
crypto/fips140-module.h
Normal file
20
crypto/fips140-module.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright 2021 Google LLC
|
||||
*/
|
||||
|
||||
#ifndef _CRYPTO_FIPS140_MODULE_H
|
||||
#define _CRYPTO_FIPS140_MODULE_H
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "fips140: " fmt
|
||||
|
||||
#ifdef CONFIG_CRYPTO_FIPS140_MOD_ERROR_INJECTION
|
||||
extern char *fips140_broken_alg;
|
||||
#endif
|
||||
|
||||
bool __init __must_check fips140_run_selftests(void);
|
||||
|
||||
#endif /* _CRYPTO_FIPS140_MODULE_H */
|
||||
867
crypto/fips140-selftests.c
Normal file
867
crypto/fips140-selftests.c
Normal file
File diff suppressed because it is too large
Load Diff
121
tools/crypto/gen_fips140_testvecs.py
Executable file
121
tools/crypto/gen_fips140_testvecs.py
Executable file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Generate most of the test vectors for the FIPS 140 cryptographic self-tests.
|
||||
#
|
||||
# Usage:
|
||||
# tools/crypto/gen_fips140_testvecs.py > crypto/fips140-generated-testvecs.h
|
||||
#
|
||||
# Prerequisites:
|
||||
# Debian: apt-get install python3-pycryptodome python3-cryptography
|
||||
# Arch Linux: pacman -S python-pycryptodomex python-cryptography
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
import os
|
||||
|
||||
import Cryptodome.Cipher.AES
|
||||
import Cryptodome.Util.Counter
|
||||
|
||||
import cryptography.hazmat.primitives.ciphers
|
||||
import cryptography.hazmat.primitives.ciphers.algorithms
|
||||
import cryptography.hazmat.primitives.ciphers.modes
|
||||
|
||||
scriptname = os.path.basename(__file__)
|
||||
|
||||
message = bytes('This is a 32-byte test message.\0', 'ascii')
|
||||
aes_key = bytes('128-bit AES key\0', 'ascii')
|
||||
aes_xts_key = bytes('This is an AES-128-XTS key.\0\0\0\0\0', 'ascii')
|
||||
aes_iv = bytes('ABCDEFGHIJKL\0\0\0\0', 'ascii')
|
||||
assoc = bytes('associated data string', 'ascii')
|
||||
hmac_key = bytes('128-bit HMAC key', 'ascii')
|
||||
|
||||
def warn_generated():
|
||||
print(f'''/*
|
||||
* This header was automatically generated by {scriptname}.
|
||||
* Don't edit it directly.
|
||||
*/''')
|
||||
|
||||
def is_string_value(value):
|
||||
return (value.isascii() and
|
||||
all(c == '\x00' or c.isprintable() for c in str(value, 'ascii')))
|
||||
|
||||
def format_value(value, is_string):
|
||||
if is_string:
|
||||
return value
|
||||
hexstr = ''
|
||||
for byte in value:
|
||||
hexstr += f'\\x{byte:02x}'
|
||||
return hexstr
|
||||
|
||||
def print_value(name, value):
|
||||
is_string = is_string_value(value)
|
||||
hdr = f'static const u8 fips_{name}[{len(value)}] __initconst ='
|
||||
print(hdr, end='')
|
||||
if is_string:
|
||||
value = str(value, 'ascii').rstrip('\x00')
|
||||
chars_per_byte = 1
|
||||
else:
|
||||
chars_per_byte = 4
|
||||
bytes_per_line = 64 // chars_per_byte
|
||||
|
||||
if len(hdr) + (chars_per_byte * len(value)) + 4 <= 80:
|
||||
print(f' "{format_value(value, is_string)}"', end='')
|
||||
else:
|
||||
for chunk in [value[i:i+bytes_per_line]
|
||||
for i in range(0, len(value), bytes_per_line)]:
|
||||
print(f'\n\t"{format_value(chunk, is_string)}"', end='')
|
||||
print(';')
|
||||
print('')
|
||||
|
||||
def generate_aes_testvecs():
|
||||
print_value('aes_key', aes_key)
|
||||
print_value('aes_iv', aes_iv)
|
||||
|
||||
cbc = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_CBC,
|
||||
iv=aes_iv)
|
||||
print_value('aes_cbc_ciphertext', cbc.encrypt(message))
|
||||
|
||||
ecb = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_ECB)
|
||||
print_value('aes_ecb_ciphertext', ecb.encrypt(message))
|
||||
|
||||
ctr = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_CTR,
|
||||
nonce=aes_iv[:12])
|
||||
print_value('aes_ctr_ciphertext', ctr.encrypt(message))
|
||||
|
||||
print_value('aes_gcm_assoc', assoc)
|
||||
gcm = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_GCM,
|
||||
nonce=aes_iv[:12], mac_len=16)
|
||||
gcm.update(assoc)
|
||||
raw_ciphertext, tag = gcm.encrypt_and_digest(message)
|
||||
print_value('aes_gcm_ciphertext', raw_ciphertext + tag)
|
||||
|
||||
# Unfortunately, pycryptodome doesn't support XTS, so for it we need to use
|
||||
# a different Python package (the "cryptography" package).
|
||||
print_value('aes_xts_key', aes_xts_key)
|
||||
xts = cryptography.hazmat.primitives.ciphers.Cipher(
|
||||
cryptography.hazmat.primitives.ciphers.algorithms.AES(aes_xts_key),
|
||||
cryptography.hazmat.primitives.ciphers.modes.XTS(aes_iv)).encryptor()
|
||||
ciphertext = xts.update(message) + xts.finalize()
|
||||
print_value('aes_xts_ciphertext', ciphertext)
|
||||
|
||||
def generate_sha_testvecs():
|
||||
print_value('hmac_key', hmac_key)
|
||||
for alg in ['sha1', 'sha256', 'hmac_sha256', 'sha512']:
|
||||
if alg.startswith('hmac_'):
|
||||
h = hmac.new(hmac_key, message, alg.removeprefix('hmac_'))
|
||||
else:
|
||||
h = hashlib.new(alg, message)
|
||||
print_value(f'{alg}_digest', h.digest())
|
||||
|
||||
print('/* SPDX-License-Identifier: GPL-2.0-only */')
|
||||
print('/* Copyright 2021 Google LLC */')
|
||||
print('')
|
||||
warn_generated()
|
||||
print('')
|
||||
print_value('message', message)
|
||||
generate_aes_testvecs()
|
||||
generate_sha_testvecs()
|
||||
warn_generated()
|
||||
Reference in New Issue
Block a user