mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-06 09:25:22 +02:00
ANDROID: fips140: test all implementations
Test all implementations of each algorithm rather than just the highest priority implementation. This aligns with the revised guidance we have received from the lab. We can still skip some tests in some cases, as per the FIPS 140-2 Implementation Guidance document. See the comments for details. To align with the new scope of the tests, the fips140.broken_alg module parameter now must specify an implementation (e.g. "sha256-ce") rather than an algorithm (e.g. "sha256"). No change to the DRBG tests is required, as it turns out the module only includes HMAC_DRBG. However, clarify the comment about the DRBG tests. On a Pixel device, this increases the running time of the fips140 tests from 0.5ms to 3.1 ms (very roughly; there's a lot of variation). This is still very fast, so it isn't expected to be a problem. Bug: 153614920 Bug: 173104584 Bug: 188620248 Change-Id: I555b535dd45f0164b7744a2c9338c501bb88de86 Signed-off-by: Eric Biggers <ebiggers@google.com> (cherry picked from commit abe07806964be48e8d0005e26f33632ace5e4152)
This commit is contained in:
parent
82c940e0e1
commit
b397a0387c
|
@ -14,10 +14,6 @@
|
|||
* is somewhat helpful. Basically, all implementations of all FIPS approved
|
||||
* algorithms (including modes of operation) must be tested. However:
|
||||
*
|
||||
* - If an implementation won't be used, it doesn't have to be tested. So
|
||||
* when multiple implementations of the same algorithm are registered with
|
||||
* the crypto API, we only have to test the default (highest-priority) one.
|
||||
*
|
||||
* - There are provisions for skipping tests that are already sufficiently
|
||||
* covered by other tests. E.g., HMAC-SHA256 may cover SHA-256.
|
||||
*
|
||||
|
@ -28,12 +24,15 @@
|
|||
*
|
||||
* - Only one key size per algorithm needs to be tested.
|
||||
*
|
||||
* There is some ambiguity about whether all implementations of each algorithm
|
||||
* must be tested, or whether it is sufficient to test just the highest priority
|
||||
* implementation. To be safe we test all implementations, except ones that can
|
||||
* be excluded by one of the rules above.
|
||||
*
|
||||
* See fips140_selftests[] for the list of tests we've selected. Currently, all
|
||||
* our test vectors except the DRBG ones were generated by the script
|
||||
* tools/crypto/gen_fips140_testvecs.py, using the known-good implementations in
|
||||
* the Python packages hashlib, pycryptodome, and cryptography. The DRBG test
|
||||
* vectors were manually extracted from
|
||||
* https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/drbg/drbgtestvectors.zip.
|
||||
* the Python packages hashlib, pycryptodome, and cryptography.
|
||||
*
|
||||
* Note that we don't reuse the upstream crypto API's self-tests
|
||||
* (crypto/testmgr.{c,h}), for several reasons:
|
||||
|
@ -54,22 +53,12 @@
|
|||
#include <crypto/aes.h>
|
||||
#include <crypto/drbg.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/internal/cipher.h>
|
||||
#include <crypto/rng.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <crypto/skcipher.h>
|
||||
|
||||
#include "fips140-module.h"
|
||||
|
||||
/* Test vector for a block cipher algorithm */
|
||||
struct blockcipher_testvec {
|
||||
const u8 *key;
|
||||
size_t key_size;
|
||||
const u8 *plaintext;
|
||||
const u8 *ciphertext;
|
||||
size_t block_size;
|
||||
};
|
||||
|
||||
/* Test vector for an AEAD algorithm */
|
||||
struct aead_testvec {
|
||||
const u8 *key;
|
||||
|
@ -121,15 +110,27 @@ struct drbg_testvec {
|
|||
size_t out_size;
|
||||
};
|
||||
|
||||
/*
|
||||
* A struct which specifies an algorithm name (using crypto API syntax), a test
|
||||
* function for that algorithm, and a test vector used by that test function.
|
||||
*/
|
||||
struct fips_test {
|
||||
/* The name of the algorithm, in crypto API syntax */
|
||||
const char *alg;
|
||||
int __must_check (*func)(const struct fips_test *test);
|
||||
|
||||
/*
|
||||
* The optional list of implementations to test. @func will be called
|
||||
* once per implementation, or once with @alg if this list is empty.
|
||||
* The implementation names must be given in crypto API syntax, or in
|
||||
* the case of a library implementation should have "-lib" appended.
|
||||
*/
|
||||
const char *impls[8];
|
||||
|
||||
/*
|
||||
* The test function. It should execute a known-answer test on an
|
||||
* algorithm implementation, using the below test vector.
|
||||
*/
|
||||
int __must_check (*func)(const struct fips_test *test,
|
||||
const char *impl);
|
||||
|
||||
/* The test vector, with a format specific to the type of algorithm */
|
||||
union {
|
||||
struct blockcipher_testvec blockcipher;
|
||||
struct aead_testvec aead;
|
||||
struct skcipher_testvec skcipher;
|
||||
struct hash_testvec hash;
|
||||
|
@ -141,17 +142,16 @@ struct fips_test {
|
|||
#define MAX_IV_SIZE 16
|
||||
|
||||
static int __init __must_check
|
||||
fips_check_result(const struct fips_test *test, u8 *result,
|
||||
const u8 *expected_result, size_t result_size,
|
||||
const char *operation)
|
||||
fips_check_result(u8 *result, const u8 *expected_result, size_t result_size,
|
||||
const char *impl, const char *operation)
|
||||
{
|
||||
#ifdef CONFIG_CRYPTO_FIPS140_MOD_ERROR_INJECTION
|
||||
/* Inject a failure (via corrupting the result) if requested. */
|
||||
if (fips140_broken_alg && strcmp(test->alg, fips140_broken_alg) == 0)
|
||||
if (fips140_broken_alg && strcmp(impl, fips140_broken_alg) == 0)
|
||||
result[0] ^= 0xff;
|
||||
#endif
|
||||
if (memcmp(result, expected_result, result_size) != 0) {
|
||||
pr_err("wrong result from %s %s\n", test->alg, operation);
|
||||
pr_err("wrong result from %s %s\n", impl, operation);
|
||||
return -EBADMSG;
|
||||
}
|
||||
return 0;
|
||||
|
@ -176,96 +176,65 @@ fips_validate_alg(const struct crypto_alg *alg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Test a block cipher using the crypto_cipher API. */
|
||||
static int __init __must_check
|
||||
fips_test_blockcipher(const struct fips_test *test)
|
||||
fips_handle_alloc_tfm_error(const char *impl, int err)
|
||||
{
|
||||
const struct blockcipher_testvec *vec = &test->blockcipher;
|
||||
struct crypto_cipher *tfm;
|
||||
u8 block[MAX_CIPHER_BLOCKSIZE];
|
||||
int err;
|
||||
if (err == -ENOENT) {
|
||||
/*
|
||||
* The requested implementation of the algorithm wasn't found.
|
||||
* This is expected if the CPU lacks a feature the
|
||||
* implementation needs, such as the ARMv8 Crypto Extensions.
|
||||
*
|
||||
* When this happens, the implementation isn't available for
|
||||
* use, so we can't test it, nor do we need to. So we just skip
|
||||
* the test.
|
||||
*/
|
||||
|
||||
if (WARN_ON(vec->block_size > MAX_CIPHER_BLOCKSIZE))
|
||||
return -EINVAL;
|
||||
/*
|
||||
* "ghash-neon" is a bit unusual in that it is only registered
|
||||
* if the CPU does *not* have a feature. Skip it silently in
|
||||
* order to avoid a confusing log message.
|
||||
*/
|
||||
if (!strcmp(impl, "gcm_base(ctr(aes-generic),ghash-neon)"))
|
||||
return 0;
|
||||
|
||||
tfm = crypto_alloc_cipher(test->alg, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
err = PTR_ERR(tfm);
|
||||
pr_err("failed to allocate %s tfm: %d\n", test->alg, err);
|
||||
return err;
|
||||
pr_info("%s is unavailable (no CPU support?), skipping testing it\n",
|
||||
impl);
|
||||
return 0;
|
||||
}
|
||||
err = fips_validate_alg(tfm->base.__crt_alg);
|
||||
if (err)
|
||||
goto out;
|
||||
if (crypto_cipher_blocksize(tfm) != vec->block_size) {
|
||||
pr_err("%s has wrong block size\n", test->alg);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = crypto_cipher_setkey(tfm, vec->key, vec->key_size);
|
||||
if (err) {
|
||||
pr_err("failed to set %s key: %d\n", test->alg, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Encrypt the plaintext, then verify the resulting ciphertext. */
|
||||
memcpy(block, vec->plaintext, vec->block_size);
|
||||
crypto_cipher_encrypt_one(tfm, block, block);
|
||||
err = fips_check_result(test, block, vec->ciphertext, vec->block_size,
|
||||
"encryption");
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Decrypt the ciphertext, then verify the resulting plaintext. */
|
||||
crypto_cipher_decrypt_one(tfm, block, block);
|
||||
err = fips_check_result(test, block, vec->plaintext, vec->block_size,
|
||||
"decryption");
|
||||
out:
|
||||
crypto_free_cipher(tfm);
|
||||
pr_err("failed to allocate %s tfm: %d\n", impl, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test for plain AES (no mode of operation). We test this separately from the
|
||||
* AES modes because the implementation of AES which is used by the "aes"
|
||||
* crypto_cipher isn't necessarily the same as that used by the AES modes such
|
||||
* as "ecb(aes)". Similarly, the aes_{encrypt,decrypt}() library functions may
|
||||
* use a different implementation as well, so we test them separately too.
|
||||
*/
|
||||
static int __init __must_check
|
||||
fips_test_aes(const struct fips_test *test)
|
||||
fips_test_aes_library(const struct fips_test *test, const char *impl)
|
||||
{
|
||||
const struct blockcipher_testvec *vec = &test->blockcipher;
|
||||
const struct skcipher_testvec *vec = &test->skcipher;
|
||||
struct crypto_aes_ctx ctx;
|
||||
u8 block[AES_BLOCK_SIZE];
|
||||
int err;
|
||||
|
||||
if (WARN_ON(vec->block_size != AES_BLOCK_SIZE))
|
||||
if (WARN_ON(vec->message_size != AES_BLOCK_SIZE))
|
||||
return -EINVAL;
|
||||
|
||||
err = fips_test_blockcipher(test);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = aes_expandkey(&ctx, vec->key, vec->key_size);
|
||||
if (err) {
|
||||
pr_err("aes_expandkey() failed: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
aes_encrypt(&ctx, block, vec->plaintext);
|
||||
err = fips_check_result(test, block, vec->ciphertext, AES_BLOCK_SIZE,
|
||||
"encryption (library API)");
|
||||
err = fips_check_result(block, vec->ciphertext, AES_BLOCK_SIZE,
|
||||
impl, "encryption");
|
||||
if (err)
|
||||
return err;
|
||||
aes_decrypt(&ctx, block, block);
|
||||
return fips_check_result(test, block, vec->plaintext, AES_BLOCK_SIZE,
|
||||
"decryption (library API)");
|
||||
return fips_check_result(block, vec->plaintext, AES_BLOCK_SIZE,
|
||||
impl, "decryption");
|
||||
}
|
||||
|
||||
/* Test a length-preserving symmetric cipher using the crypto_skcipher API. */
|
||||
static int __init __must_check
|
||||
fips_test_skcipher(const struct fips_test *test)
|
||||
fips_test_skcipher(const struct fips_test *test, const char *impl)
|
||||
{
|
||||
const struct skcipher_testvec *vec = &test->skcipher;
|
||||
struct crypto_skcipher *tfm;
|
||||
|
@ -277,18 +246,17 @@ fips_test_skcipher(const struct fips_test *test)
|
|||
|
||||
if (WARN_ON(vec->iv_size > MAX_IV_SIZE))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(vec->message_size <= 0))
|
||||
return -EINVAL;
|
||||
|
||||
tfm = crypto_alloc_skcipher(test->alg, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
err = PTR_ERR(tfm);
|
||||
pr_err("failed to allocate %s tfm: %d\n", test->alg, err);
|
||||
return err;
|
||||
}
|
||||
tfm = crypto_alloc_skcipher(impl, 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return fips_handle_alloc_tfm_error(impl, PTR_ERR(tfm));
|
||||
err = fips_validate_alg(&crypto_skcipher_alg(tfm)->base);
|
||||
if (err)
|
||||
goto out;
|
||||
if (crypto_skcipher_ivsize(tfm) != vec->iv_size) {
|
||||
pr_err("%s has wrong IV size\n", test->alg);
|
||||
pr_err("%s has wrong IV size\n", impl);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -307,7 +275,7 @@ fips_test_skcipher(const struct fips_test *test)
|
|||
|
||||
err = crypto_skcipher_setkey(tfm, vec->key, vec->key_size);
|
||||
if (err) {
|
||||
pr_err("failed to set %s key: %d\n", test->alg, err);
|
||||
pr_err("failed to set %s key: %d\n", impl, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -315,11 +283,11 @@ fips_test_skcipher(const struct fips_test *test)
|
|||
memcpy(iv, vec->iv, vec->iv_size);
|
||||
err = crypto_skcipher_encrypt(req);
|
||||
if (err) {
|
||||
pr_err("%s encryption failed: %d\n", test->alg, err);
|
||||
pr_err("%s encryption failed: %d\n", impl, err);
|
||||
goto out;
|
||||
}
|
||||
err = fips_check_result(test, message, vec->ciphertext,
|
||||
vec->message_size, "encryption");
|
||||
err = fips_check_result(message, vec->ciphertext, vec->message_size,
|
||||
impl, "encryption");
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
@ -327,11 +295,11 @@ fips_test_skcipher(const struct fips_test *test)
|
|||
memcpy(iv, vec->iv, vec->iv_size);
|
||||
err = crypto_skcipher_decrypt(req);
|
||||
if (err) {
|
||||
pr_err("%s decryption failed: %d\n", test->alg, err);
|
||||
pr_err("%s decryption failed: %d\n", impl, err);
|
||||
goto out;
|
||||
}
|
||||
err = fips_check_result(test, message, vec->plaintext,
|
||||
vec->message_size, "decryption");
|
||||
err = fips_check_result(message, vec->plaintext, vec->message_size,
|
||||
impl, "decryption");
|
||||
out:
|
||||
kfree(message);
|
||||
skcipher_request_free(req);
|
||||
|
@ -341,7 +309,7 @@ out:
|
|||
|
||||
/* Test an AEAD using the crypto_aead API. */
|
||||
static int __init __must_check
|
||||
fips_test_aead(const struct fips_test *test)
|
||||
fips_test_aead(const struct fips_test *test, const char *impl)
|
||||
{
|
||||
const struct aead_testvec *vec = &test->aead;
|
||||
const int tag_size = vec->ciphertext_size - vec->plaintext_size;
|
||||
|
@ -359,17 +327,14 @@ fips_test_aead(const struct fips_test *test)
|
|||
if (WARN_ON(vec->ciphertext_size <= vec->plaintext_size))
|
||||
return -EINVAL;
|
||||
|
||||
tfm = crypto_alloc_aead(test->alg, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
err = PTR_ERR(tfm);
|
||||
pr_err("failed to allocate %s tfm: %d\n", test->alg, err);
|
||||
return err;
|
||||
}
|
||||
tfm = crypto_alloc_aead(impl, 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return fips_handle_alloc_tfm_error(impl, PTR_ERR(tfm));
|
||||
err = fips_validate_alg(&crypto_aead_alg(tfm)->base);
|
||||
if (err)
|
||||
goto out;
|
||||
if (crypto_aead_ivsize(tfm) != vec->iv_size) {
|
||||
pr_err("%s has wrong IV size\n", test->alg);
|
||||
pr_err("%s has wrong IV size\n", impl);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -393,14 +358,14 @@ fips_test_aead(const struct fips_test *test)
|
|||
|
||||
err = crypto_aead_setkey(tfm, vec->key, vec->key_size);
|
||||
if (err) {
|
||||
pr_err("failed to set %s key: %d\n", test->alg, err);
|
||||
pr_err("failed to set %s key: %d\n", impl, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = crypto_aead_setauthsize(tfm, tag_size);
|
||||
if (err) {
|
||||
pr_err("failed to set %s authentication tag size: %d\n",
|
||||
test->alg, err);
|
||||
impl, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -412,11 +377,11 @@ fips_test_aead(const struct fips_test *test)
|
|||
aead_request_set_crypt(req, sg, sg, vec->plaintext_size, iv);
|
||||
err = crypto_aead_encrypt(req);
|
||||
if (err) {
|
||||
pr_err("%s encryption failed: %d\n", test->alg, err);
|
||||
pr_err("%s encryption failed: %d\n", impl, err);
|
||||
goto out;
|
||||
}
|
||||
err = fips_check_result(test, message, vec->ciphertext,
|
||||
vec->ciphertext_size, "encryption");
|
||||
err = fips_check_result(message, vec->ciphertext, vec->ciphertext_size,
|
||||
impl, "encryption");
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
@ -428,11 +393,11 @@ fips_test_aead(const struct fips_test *test)
|
|||
aead_request_set_crypt(req, sg, sg, vec->ciphertext_size, iv);
|
||||
err = crypto_aead_decrypt(req);
|
||||
if (err) {
|
||||
pr_err("%s decryption failed: %d\n", test->alg, err);
|
||||
pr_err("%s decryption failed: %d\n", impl, err);
|
||||
goto out;
|
||||
}
|
||||
err = fips_check_result(test, message, vec->plaintext,
|
||||
vec->plaintext_size, "decryption");
|
||||
err = fips_check_result(message, vec->plaintext, vec->plaintext_size,
|
||||
impl, "decryption");
|
||||
out:
|
||||
kfree(message);
|
||||
kfree(assoc);
|
||||
|
@ -449,7 +414,7 @@ out:
|
|||
* be no hash algorithms that can be accessed only through crypto_ahash.
|
||||
*/
|
||||
static int __init __must_check
|
||||
fips_test_hash(const struct fips_test *test)
|
||||
fips_test_hash(const struct fips_test *test, const char *impl)
|
||||
{
|
||||
const struct hash_testvec *vec = &test->hash;
|
||||
struct crypto_shash *tfm;
|
||||
|
@ -459,17 +424,14 @@ fips_test_hash(const struct fips_test *test)
|
|||
if (WARN_ON(vec->digest_size > HASH_MAX_DIGESTSIZE))
|
||||
return -EINVAL;
|
||||
|
||||
tfm = crypto_alloc_shash(test->alg, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
err = PTR_ERR(tfm);
|
||||
pr_err("failed to allocate %s tfm: %d\n", test->alg, err);
|
||||
return err;
|
||||
}
|
||||
tfm = crypto_alloc_shash(impl, 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return fips_handle_alloc_tfm_error(impl, PTR_ERR(tfm));
|
||||
err = fips_validate_alg(&crypto_shash_alg(tfm)->base);
|
||||
if (err)
|
||||
goto out;
|
||||
if (crypto_shash_digestsize(tfm) != vec->digest_size) {
|
||||
pr_err("%s has wrong digest size\n", test->alg);
|
||||
pr_err("%s has wrong digest size\n", impl);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -477,7 +439,7 @@ fips_test_hash(const struct fips_test *test)
|
|||
if (vec->key) {
|
||||
err = crypto_shash_setkey(tfm, vec->key, vec->key_size);
|
||||
if (err) {
|
||||
pr_err("failed to set %s key: %d\n", test->alg, err);
|
||||
pr_err("failed to set %s key: %d\n", impl, err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
@ -485,22 +447,18 @@ fips_test_hash(const struct fips_test *test)
|
|||
err = crypto_shash_tfm_digest(tfm, vec->message, vec->message_size,
|
||||
digest);
|
||||
if (err) {
|
||||
pr_err("%s digest computation failed: %d\n", test->alg, err);
|
||||
pr_err("%s digest computation failed: %d\n", impl, err);
|
||||
goto out;
|
||||
}
|
||||
err = fips_check_result(test, digest, vec->digest, vec->digest_size,
|
||||
"digest");
|
||||
err = fips_check_result(digest, vec->digest, vec->digest_size,
|
||||
impl, "digest");
|
||||
out:
|
||||
crypto_free_shash(tfm);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test the sha256() library function, as it may not be covered by the "sha256"
|
||||
* crypto_shash, and thus may not be covered by the "hmac(sha256)" test we do.
|
||||
*/
|
||||
static int __init __must_check
|
||||
fips_test_sha256_library(const struct fips_test *test)
|
||||
fips_test_sha256_library(const struct fips_test *test, const char *impl)
|
||||
{
|
||||
const struct hash_testvec *vec = &test->hash;
|
||||
u8 digest[SHA256_DIGEST_SIZE];
|
||||
|
@ -509,13 +467,13 @@ fips_test_sha256_library(const struct fips_test *test)
|
|||
return -EINVAL;
|
||||
|
||||
sha256(vec->message, vec->message_size, digest);
|
||||
return fips_check_result(test, digest, vec->digest, vec->digest_size,
|
||||
"digest (library API)");
|
||||
return fips_check_result(digest, vec->digest, vec->digest_size,
|
||||
impl, "digest");
|
||||
}
|
||||
|
||||
/* Test a DRBG using the crypto_rng API. */
|
||||
static int __init __must_check
|
||||
fips_test_drbg(const struct fips_test *test)
|
||||
fips_test_drbg(const struct fips_test *test, const char *impl)
|
||||
{
|
||||
const struct drbg_testvec *vec = &test->drbg;
|
||||
struct crypto_rng *rng;
|
||||
|
@ -524,12 +482,9 @@ fips_test_drbg(const struct fips_test *test)
|
|||
struct drbg_string addtl, pers, testentropy;
|
||||
int err;
|
||||
|
||||
rng = crypto_alloc_rng(test->alg, 0, 0);
|
||||
if (IS_ERR(rng)) {
|
||||
err = PTR_ERR(rng);
|
||||
pr_err("failed to allocate %s tfm: %d\n", test->alg, err);
|
||||
return PTR_ERR(rng);
|
||||
}
|
||||
rng = crypto_alloc_rng(impl, 0, 0);
|
||||
if (IS_ERR(rng))
|
||||
return fips_handle_alloc_tfm_error(impl, PTR_ERR(rng));
|
||||
err = fips_validate_alg(&crypto_rng_alg(rng)->base);
|
||||
if (err)
|
||||
goto out;
|
||||
|
@ -549,7 +504,7 @@ fips_test_drbg(const struct fips_test *test)
|
|||
drbg_string_fill(&pers, vec->pers, vec->pers_size);
|
||||
err = crypto_drbg_reset_test(rng, &pers, &test_data);
|
||||
if (err) {
|
||||
pr_err("failed to reset %s\n", test->alg);
|
||||
pr_err("failed to reset %s\n", impl);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -570,7 +525,7 @@ fips_test_drbg(const struct fips_test *test)
|
|||
}
|
||||
if (err) {
|
||||
pr_err("failed to get bytes from %s (try 1): %d\n",
|
||||
test->alg, err);
|
||||
impl, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -590,13 +545,13 @@ fips_test_drbg(const struct fips_test *test)
|
|||
}
|
||||
if (err) {
|
||||
pr_err("failed to get bytes from %s (try 2): %d\n",
|
||||
test->alg, err);
|
||||
impl, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check that the DRBG generated the expected output. */
|
||||
err = fips_check_result(test, output, vec->output, vec->out_size,
|
||||
"get_bytes");
|
||||
err = fips_check_result(output, vec->output, vec->out_size,
|
||||
impl, "get_bytes");
|
||||
out:
|
||||
kfree(output);
|
||||
crypto_free_rng(rng);
|
||||
|
@ -606,33 +561,134 @@ out:
|
|||
/* Include the test vectors generated by the Python script. */
|
||||
#include "fips140-generated-testvecs.h"
|
||||
|
||||
/* List of all self-tests. Keep this in sync with fips140_algorithms[]. */
|
||||
/*
|
||||
* List of all self-tests. Keep this in sync with fips140_algorithms[].
|
||||
*
|
||||
* When possible, we have followed the FIPS 140-2 Implementation Guidance (IG)
|
||||
* document when creating this list of tests. The result is intended to be a
|
||||
* list of tests that is near-minimal (and thus minimizes runtime overhead)
|
||||
* while complying with all requirements. For additional details, see the
|
||||
* comment at the beginning of this file.
|
||||
*/
|
||||
static const struct fips_test fips140_selftests[] __initconst = {
|
||||
/*
|
||||
* Tests for AES and AES modes.
|
||||
* Test for the AES library API.
|
||||
*
|
||||
* The full list of AES algorithms we potentially need to test are AES
|
||||
* by itself, AES-CBC, AES-CTR, AES-ECB, AES-GCM, and AES-XTS. We can
|
||||
* follow the FIPS 140-2 Implementation Guidance (IG) document to try to
|
||||
* reduce this list, but we run into the issue that the architecture-
|
||||
* specific implementations of these algorithms in Linux often don't
|
||||
* share the "same" underlying AES implementation. E.g., the ARMv8 CE
|
||||
* optimized implementations issue ARMv8 CE instructions directly rather
|
||||
* than going through a separate AES implementation. In this case,
|
||||
* separate tests are needed according to section 9.2 of the IG.
|
||||
* Since the AES library API may use its own AES implementation and the
|
||||
* module provides no support for composing it with a mode of operation
|
||||
* (it's just plain AES), we must test it directly.
|
||||
*
|
||||
* In contrast, we don't need to directly test the "aes" ciphers that
|
||||
* are accessible through the crypto_cipher API (e.g. "aes-ce"), as they
|
||||
* are covered indirectly by AES-GCM and AES-ECB tests.
|
||||
*/
|
||||
{
|
||||
.alg = "aes",
|
||||
.func = fips_test_aes,
|
||||
.blockcipher = {
|
||||
.impls = {"aes-lib"},
|
||||
.func = fips_test_aes_library,
|
||||
.skcipher = {
|
||||
.key = fips_aes_key,
|
||||
.key_size = sizeof(fips_aes_key),
|
||||
.plaintext = fips_message,
|
||||
.ciphertext = fips_aes_ecb_ciphertext,
|
||||
.block_size = 16,
|
||||
.message_size = 16,
|
||||
}
|
||||
}, {
|
||||
},
|
||||
/*
|
||||
* Tests for AES-GCM, a.k.a. "gcm(aes)" in crypto API syntax.
|
||||
*
|
||||
* The IG requires that each underlying AES implementation be tested in
|
||||
* an authenticated mode, if implemented. We therefore must test the
|
||||
* "gcm" template composed with each "aes" implementation.
|
||||
*
|
||||
* We also must test all standalone implementations of "gcm(aes)" such
|
||||
* as "gcm-aes-ce", as they don't reuse another full AES implementation
|
||||
* and thus can't be covered by another test.
|
||||
*/
|
||||
{
|
||||
.alg = "gcm(aes)",
|
||||
.impls = {
|
||||
/* "gcm" template with all "aes" implementations */
|
||||
"gcm_base(ctr(aes-generic),ghash-generic)",
|
||||
"gcm_base(ctr(aes-arm64),ghash-generic)",
|
||||
"gcm_base(ctr(aes-ce),ghash-generic)",
|
||||
/*
|
||||
* "gcm" template with alternate "ghash" implementation.
|
||||
* The IG doesn't consider multiple GHASH
|
||||
* implementations, but we include this to be safe.
|
||||
*/
|
||||
"gcm_base(ctr(aes-generic),ghash-neon)",
|
||||
/* All standalone implementations of "gcm(aes)" */
|
||||
"gcm-aes-ce",
|
||||
},
|
||||
.func = fips_test_aead,
|
||||
.aead = {
|
||||
.key = fips_aes_key,
|
||||
.key_size = sizeof(fips_aes_key),
|
||||
.iv = fips_aes_iv,
|
||||
/* The GCM implementations assume an IV size of 12. */
|
||||
.iv_size = 12,
|
||||
.assoc = fips_aes_gcm_assoc,
|
||||
.assoc_size = sizeof(fips_aes_gcm_assoc),
|
||||
.plaintext = fips_message,
|
||||
.plaintext_size = sizeof(fips_message),
|
||||
.ciphertext = fips_aes_gcm_ciphertext,
|
||||
.ciphertext_size = sizeof(fips_aes_gcm_ciphertext),
|
||||
}
|
||||
},
|
||||
/*
|
||||
* Tests for AES-ECB, a.k.a. "ecb(aes)" in crypto API syntax.
|
||||
*
|
||||
* The IG requires that each underlying AES implementation be tested in
|
||||
* a mode that exercises the encryption direction of AES and in a mode
|
||||
* that exercises the decryption direction of AES. GCM only covers the
|
||||
* encryption direction, so we add ECB to test decryption. We therefore
|
||||
* test the "ecb" template composed with each "aes" implementation.
|
||||
*
|
||||
* We also must test all standalone implementations of "ecb(aes)" such
|
||||
* as "ecb-aes-ce", as they don't reuse another full AES implementation
|
||||
* and thus can't be covered by another test.
|
||||
*/
|
||||
{
|
||||
.alg = "ecb(aes)",
|
||||
.impls = {
|
||||
/* "ecb" template with all "aes" implementations */
|
||||
"ecb(aes-generic)",
|
||||
"ecb(aes-arm64)",
|
||||
"ecb(aes-ce)",
|
||||
/* All standalone implementations of "ecb(aes)" */
|
||||
"ecb-aes-neon",
|
||||
"ecb-aes-neonbs",
|
||||
"ecb-aes-ce",
|
||||
},
|
||||
.func = fips_test_skcipher,
|
||||
.skcipher = {
|
||||
.key = fips_aes_key,
|
||||
.key_size = sizeof(fips_aes_key),
|
||||
.plaintext = fips_message,
|
||||
.ciphertext = fips_aes_ecb_ciphertext,
|
||||
.message_size = sizeof(fips_message)
|
||||
}
|
||||
},
|
||||
/*
|
||||
* Tests for AES-CBC, AES-CTR, and AES-XTS.
|
||||
*
|
||||
* According to the IG, unauthenticated AES modes don't need to have
|
||||
* their own test as long as both directions of the underlying AES
|
||||
* implementation are already tested via other modes.
|
||||
*
|
||||
* However we must still test standalone implementations of these modes,
|
||||
* as they don't reuse another full AES implementation and thus can't be
|
||||
* covered by another test.
|
||||
*/
|
||||
{
|
||||
.alg = "cbc(aes)",
|
||||
.impls = {
|
||||
/* All standalone implementations of "cbc(aes)" */
|
||||
"cbc-aes-neon",
|
||||
"cbc-aes-neonbs",
|
||||
"cbc-aes-ce",
|
||||
},
|
||||
.func = fips_test_skcipher,
|
||||
.skcipher = {
|
||||
.key = fips_aes_key,
|
||||
|
@ -645,6 +701,12 @@ static const struct fips_test fips140_selftests[] __initconst = {
|
|||
}
|
||||
}, {
|
||||
.alg = "ctr(aes)",
|
||||
.impls = {
|
||||
/* All standalone implementations of "ctr(aes)" */
|
||||
"ctr-aes-neon",
|
||||
"ctr-aes-neonbs",
|
||||
"ctr-aes-ce",
|
||||
},
|
||||
.func = fips_test_skcipher,
|
||||
.skcipher = {
|
||||
.key = fips_aes_key,
|
||||
|
@ -655,34 +717,14 @@ static const struct fips_test fips140_selftests[] __initconst = {
|
|||
.ciphertext = fips_aes_ctr_ciphertext,
|
||||
.message_size = sizeof(fips_message),
|
||||
}
|
||||
}, {
|
||||
.alg = "ecb(aes)",
|
||||
.func = fips_test_skcipher,
|
||||
.skcipher = {
|
||||
.key = fips_aes_key,
|
||||
.key_size = sizeof(fips_aes_key),
|
||||
.plaintext = fips_message,
|
||||
.ciphertext = fips_aes_ecb_ciphertext,
|
||||
.message_size = sizeof(fips_message)
|
||||
}
|
||||
}, {
|
||||
.alg = "gcm(aes)",
|
||||
.func = fips_test_aead,
|
||||
.aead = {
|
||||
.key = fips_aes_key,
|
||||
.key_size = sizeof(fips_aes_key),
|
||||
.iv = fips_aes_iv,
|
||||
/* The GCM implementation assumes an IV size of 12. */
|
||||
.iv_size = 12,
|
||||
.assoc = fips_aes_gcm_assoc,
|
||||
.assoc_size = sizeof(fips_aes_gcm_assoc),
|
||||
.plaintext = fips_message,
|
||||
.plaintext_size = sizeof(fips_message),
|
||||
.ciphertext = fips_aes_gcm_ciphertext,
|
||||
.ciphertext_size = sizeof(fips_aes_gcm_ciphertext),
|
||||
}
|
||||
}, {
|
||||
.alg = "xts(aes)",
|
||||
.impls = {
|
||||
/* All standalone implementations of "xts(aes)" */
|
||||
"xts-aes-neon",
|
||||
"xts-aes-neonbs",
|
||||
"xts-aes-ce",
|
||||
},
|
||||
.func = fips_test_skcipher,
|
||||
.skcipher = {
|
||||
.key = fips_aes_xts_key,
|
||||
|
@ -693,27 +735,16 @@ static const struct fips_test fips140_selftests[] __initconst = {
|
|||
.ciphertext = fips_aes_xts_ciphertext,
|
||||
.message_size = sizeof(fips_message),
|
||||
}
|
||||
/*
|
||||
* Tests for SHA-1, SHA-256, HMAC-SHA256, and SHA-512.
|
||||
*
|
||||
* The selection of these specific tests follows the guidance from
|
||||
* section 9 of the FIPS 140-2 Implementation Guidance (IG) document to
|
||||
* achieve a minimal list of tests, rather than testing all of
|
||||
* SHA-{1,224,256,384,512} and HMAC-SHA{1,224,256,384,512}. As per the
|
||||
* IG, testing SHA-224 is only required if SHA-256 isn't implemented,
|
||||
* and testing SHA-384 is only required if SHA-512 isn't implemented.
|
||||
* Also, HMAC only has to be tested with one underlying SHA, and the
|
||||
* HMAC test also fulfills the test for its underlying SHA. That would
|
||||
* result in a test list of e.g. SHA-1, HMAC-SHA256, and SHA-512.
|
||||
*
|
||||
* However we also need to take into account cases where implementations
|
||||
* aren't shared in the "natural" way assumed by the IG. Currently the
|
||||
* only known exception w.r.t. SHA-* and HMAC-* is the sha256() library
|
||||
* function which may not be covered by the test of the "hmac(sha256)"
|
||||
* crypto_shash. So, we test sha256() separately.
|
||||
*/
|
||||
}, {
|
||||
},
|
||||
|
||||
/* Tests for SHA-1 */
|
||||
{
|
||||
.alg = "sha1",
|
||||
.impls = {
|
||||
/* All implementations of "sha1" */
|
||||
"sha1-generic",
|
||||
"sha1-ce"
|
||||
},
|
||||
.func = fips_test_hash,
|
||||
.hash = {
|
||||
.message = fips_message,
|
||||
|
@ -721,8 +752,35 @@ static const struct fips_test fips140_selftests[] __initconst = {
|
|||
.digest = fips_sha1_digest,
|
||||
.digest_size = sizeof(fips_sha1_digest)
|
||||
}
|
||||
}, {
|
||||
},
|
||||
/*
|
||||
* Tests for all SHA-256 implementations other than the sha256() library
|
||||
* function. As per the IG, these tests also fulfill the tests for the
|
||||
* corresponding SHA-224 implementations.
|
||||
*/
|
||||
{
|
||||
.alg = "sha256",
|
||||
.impls = {
|
||||
/* All implementations of "sha256" */
|
||||
"sha256-generic",
|
||||
"sha256-arm64",
|
||||
"sha256-ce",
|
||||
},
|
||||
.func = fips_test_hash,
|
||||
.hash = {
|
||||
.message = fips_message,
|
||||
.message_size = sizeof(fips_message),
|
||||
.digest = fips_sha256_digest,
|
||||
.digest_size = sizeof(fips_sha256_digest)
|
||||
}
|
||||
},
|
||||
/*
|
||||
* Test for the sha256() library function. This must be tested
|
||||
* separately because it may use its own SHA-256 implementation.
|
||||
*/
|
||||
{
|
||||
.alg = "sha256",
|
||||
.impls = {"sha256-lib"},
|
||||
.func = fips_test_sha256_library,
|
||||
.hash = {
|
||||
.message = fips_message,
|
||||
|
@ -730,7 +788,36 @@ static const struct fips_test fips140_selftests[] __initconst = {
|
|||
.digest = fips_sha256_digest,
|
||||
.digest_size = sizeof(fips_sha256_digest)
|
||||
}
|
||||
}, {
|
||||
},
|
||||
/*
|
||||
* Tests for all SHA-512 implementations. As per the IG, these tests
|
||||
* also fulfill the tests for the corresponding SHA-384 implementations.
|
||||
*/
|
||||
{
|
||||
.alg = "sha512",
|
||||
.impls = {
|
||||
/* All implementations of "sha512" */
|
||||
"sha512-generic",
|
||||
"sha512-arm64",
|
||||
"sha512-ce",
|
||||
},
|
||||
.func = fips_test_hash,
|
||||
.hash = {
|
||||
.message = fips_message,
|
||||
.message_size = sizeof(fips_message),
|
||||
.digest = fips_sha512_digest,
|
||||
.digest_size = sizeof(fips_sha512_digest)
|
||||
}
|
||||
},
|
||||
/*
|
||||
* Test for HMAC. As per the IG, only one HMAC test is required,
|
||||
* provided that the same HMAC code is shared by all HMAC-SHA*. This is
|
||||
* true in our case. We choose HMAC-SHA256 for the test.
|
||||
*
|
||||
* Note that as per the IG, this can fulfill the test for the underlying
|
||||
* SHA. However, we don't currently rely on this.
|
||||
*/
|
||||
{
|
||||
.alg = "hmac(sha256)",
|
||||
.func = fips_test_hash,
|
||||
.hash = {
|
||||
|
@ -741,28 +828,37 @@ static const struct fips_test fips140_selftests[] __initconst = {
|
|||
.digest = fips_hmac_sha256_digest,
|
||||
.digest_size = sizeof(fips_hmac_sha256_digest)
|
||||
}
|
||||
}, {
|
||||
.alg = "sha512",
|
||||
.func = fips_test_hash,
|
||||
.hash = {
|
||||
.message = fips_message,
|
||||
.message_size = sizeof(fips_message),
|
||||
.digest = fips_sha512_digest,
|
||||
.digest_size = sizeof(fips_sha512_digest)
|
||||
}
|
||||
},
|
||||
/*
|
||||
* Tests for DRBG algorithms.
|
||||
* Known-answer tests for the SP800-90A DRBG algorithms.
|
||||
*
|
||||
* Only the default variant (the one that users get when they request
|
||||
* "stdrng") is required to be tested, as we don't consider the other
|
||||
* variants to be used / usable in the FIPS security policy. This is
|
||||
* similar to how e.g. we don't test both "xts(aes-generic)" and
|
||||
* "xts-aes-ce" but rather just "xts(aes)".
|
||||
* These test vectors were manually extracted from
|
||||
* https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/drbg/drbgtestvectors.zip.
|
||||
*
|
||||
* Currently the default one is "drbg_nopr_hmac_sha256"; however, just
|
||||
* in case we also test the prediction-resistant enabled variant too.
|
||||
* The selection of these tests follows the FIPS 140-2 IG as well as
|
||||
* Section 11 of SP800-90A:
|
||||
*
|
||||
* - We must test all DRBG types (HMAC, Hash, and CTR) that the module
|
||||
* implements. However, currently the module only implements
|
||||
* HMAC_DRBG (since CONFIG_CRYPTO_DRBG_CTR and CONFIG_CRYPTO_DRBG_HASH
|
||||
* aren't enabled). Therefore, we only need to test HMAC_DRBG.
|
||||
*
|
||||
* - We only need to test one HMAC variant.
|
||||
*
|
||||
* - We must test all DRBG operations: Instantiate(), Reseed(), and
|
||||
* Generate(). However, a single test sequence with a single output
|
||||
* comparison may cover all three operations, and this is what we do.
|
||||
* Note that Reseed() happens implicitly via the use of the additional
|
||||
* input and also via the use of prediction resistance when enabled.
|
||||
*
|
||||
* - The personalization string, additional input, and prediction
|
||||
* resistance support must be tested. Therefore we have chosen test
|
||||
* vectors that have a nonempty personalization string and nonempty
|
||||
* additional input, and we test the prediction-resistant variant.
|
||||
* Just in case, we also test the non-prediction-resistant variant;
|
||||
* however, it's unclear whether it's required.
|
||||
*/
|
||||
}, {
|
||||
{
|
||||
.alg = "drbg_nopr_hmac_sha256",
|
||||
.func = fips_test_drbg,
|
||||
.drbg = {
|
||||
|
@ -845,19 +941,44 @@ static const struct fips_test fips140_selftests[] __initconst = {
|
|||
}
|
||||
};
|
||||
|
||||
static int __init __must_check
|
||||
fips_run_test(const struct fips_test *test)
|
||||
{
|
||||
int i;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* If no implementations were specified, then just test the default one.
|
||||
* Otherwise, test the specified list of implementations.
|
||||
*/
|
||||
|
||||
if (test->impls[0] == NULL) {
|
||||
err = test->func(test, test->alg);
|
||||
if (err)
|
||||
pr_emerg("self-tests failed for algorithm %s: %d\n",
|
||||
test->alg, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test->impls) && test->impls[i] != NULL;
|
||||
i++) {
|
||||
err = test->func(test, test->impls[i]);
|
||||
if (err) {
|
||||
pr_emerg("self-tests failed for algorithm %s, implementation %s: %d\n",
|
||||
test->alg, test->impls[i], err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool __init fips140_run_selftests(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
pr_info("running self-tests\n");
|
||||
for (i = 0; i < ARRAY_SIZE(fips140_selftests); i++) {
|
||||
const struct fips_test *test = &fips140_selftests[i];
|
||||
int err;
|
||||
|
||||
err = test->func(test);
|
||||
if (err) {
|
||||
pr_emerg("self-tests failed for algorithm %s: %d\n",
|
||||
test->alg, err);
|
||||
if (fips_run_test(&fips140_selftests[i]) != 0) {
|
||||
/* The caller is responsible for calling panic(). */
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user