kernel_samsung_a53x/crypto/fips140_test.c
2024-06-15 16:02:09 -03:00

714 lines
16 KiB
C
Executable file

#include <crypto/aead.h>
#include <crypto/hash.h>
#include <crypto/skcipher.h>
#include <crypto/drbg.h>
#include <linux/scatterlist.h>
#include <linux/err.h>
#include "fips140.h"
#include "fips140_test.h"
extern const cipher_test_suite_t aes_ecb_tv;
extern const cipher_test_suite_t aes_cbc_tv;
extern const aead_test_suite_t aes_gcm_tv;
extern const hash_test_suite_t sha1_tv;
extern const hash_test_suite_t sha224_tv;
extern const hash_test_suite_t sha256_tv;
extern const hash_test_suite_t sha384_tv;
extern const hash_test_suite_t sha512_tv;
extern const hash_test_suite_t hmac_sha1_tv;
extern const hash_test_suite_t hmac_sha224_tv;
extern const hash_test_suite_t hmac_sha256_tv;
extern const hash_test_suite_t hmac_sha384_tv;
extern const hash_test_suite_t hmac_sha512_tv;
extern const drbg_test_suite_t drbg_pr_hmac_sha256_tv;
extern const drbg_test_suite_t drbg_nopr_hmac_sha256_tv;
// TODO: to be removed
#if defined (__clang__)
#pragma clang optimize off
#elif defined (__GNUC__)
#pragma GCC push_options
#pragma GCC optimize ("O0")
#endif
#ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST
static char *fips_functest_mode;
static char *fips_functest_KAT_list[] = {
"integrity",
"ecb(aes-generic)",
"cbc(aes-generic)",
"gcm_base(ctr(aes-generic),ghash-generic)",
"ecb(aes-ce)",
"cbc(aes-ce)",
"gcm_base(ctr(aes-ce),ghash-generic)",
"sha1-generic",
"hmac(sha1-generic)",
"sha1-ce",
"hmac(sha1-ce)",
"sha224-generic",
"sha256-generic",
"hmac(sha224-generic)",
"hmac(sha256-generic)",
"sha224-ce",
"sha256-ce",
"hmac(sha224-ce)",
"hmac(sha256-ce)",
"sha384-generic",
"sha512-generic",
"hmac(sha384-generic)",
"hmac(sha512-generic)",
"drbg_nopr_hmac_sha256",
"drbg_pr_hmac_sha256",
};
static char *fips_functest_conditional_list[] = {
"zeroization"
};
// This function is added to change fips_functest_KAT_num from tcrypt.c
void set_fips_functest_KAT_mode(const int num)
{
if (num >= 0 && num < SKC_FUNCTEST_KAT_CASE_NUM)
fips_functest_mode = fips_functest_KAT_list[num];
else
fips_functest_mode = SKC_FUNCTEST_NO_TEST;
}
EXPORT_SYMBOL_GPL(set_fips_functest_KAT_mode);
void set_fips_functest_conditional_mode(const int num)
{
if (num >= 0 && num < SKC_FUNCTEST_CONDITIONAL_CASE_NUM)
fips_functest_mode = fips_functest_conditional_list[num];
else
fips_functest_mode = SKC_FUNCTEST_NO_TEST;
}
EXPORT_SYMBOL_GPL(set_fips_functest_conditional_mode);
char *get_fips_functest_mode(void)
{
if (fips_functest_mode)
return fips_functest_mode;
else
return SKC_FUNCTEST_NO_TEST;
}
EXPORT_SYMBOL_GPL(get_fips_functest_mode);
#endif // CONFIG_CRYPTO_FIPS_FUNC_TEST
#if defined ADD_CUSTOM_KVMALLOC
static inline void *kvmalloc(size_t size, gfp_t flags){
void *ret;
ret = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN);
if (!ret) {
if (flags & __GFP_ZERO)
ret = vzalloc(size);
else
ret = vmalloc(size);
}
return ret;
}
#endif
struct tcrypt_result {
struct completion completion;
int err;
};
static void crypt_complete(struct crypto_async_request *req, int err)
{
struct tcrypt_result *res = req->data;
if (err == -EINPROGRESS)
return;
res->err = err;
complete(&res->completion);
}
static int __test_skcipher(struct crypto_skcipher *tfm,
int enc,
const cipher_testvec_t *tv)
{
#ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST
const char *algo = crypto_tfm_alg_driver_name(crypto_skcipher_tfm(tfm));
#endif
struct skcipher_request *req = NULL;
struct tcrypt_result result;
struct scatterlist sg_src;
struct scatterlist sg_dst;
int ret = -EINVAL;
uint8_t *__out_buf = NULL;
uint8_t *__in_buf = NULL;
uint8_t __iv[FIPS140_MAX_LEN_IV] = {0,};
const uint8_t *__in = NULL;
const uint8_t *__out = NULL;
__out_buf = kvmalloc(FIPS140_MAX_LEN_PCTEXT, GFP_KERNEL);
__in_buf = kvmalloc(FIPS140_MAX_LEN_PCTEXT, GFP_KERNEL);
if ((!__out_buf) ||
(!__in_buf)) {
ret = -ENOMEM;
goto out;
}
__in = enc ? tv->ptext : tv->ctext;
__out = enc ? tv->ctext : tv->ptext;
memcpy(__in_buf, __in, tv->len);
if (tv->iv_len)
memcpy(__iv, tv->iv, tv->iv_len);
else
memset(__iv, 0x00, FIPS140_MAX_LEN_IV);
init_completion(&result.completion);
req = skcipher_request_alloc(tfm, GFP_KERNEL);
if (!req)
goto out;
skcipher_request_set_callback(req,
CRYPTO_TFM_REQ_MAY_BACKLOG,
crypt_complete,
&result);
#ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST
if (!strcmp(algo, get_fips_functest_mode())) {
unsigned char temp_key[512];
memcpy(temp_key, tv->key, tv->klen);
temp_key[0] += 1;
ret = crypto_skcipher_setkey(tfm, temp_key, tv->klen);
} else {
ret = crypto_skcipher_setkey(tfm, tv->key, tv->klen);
}
#else
ret = crypto_skcipher_setkey(tfm, tv->key, tv->klen);
#endif
if (ret)
goto out;
sg_init_one(&sg_src, __in_buf, tv->len);
sg_init_one(&sg_dst, __out_buf, tv->len);
skcipher_request_set_crypt(req,
&sg_src,
&sg_dst,
tv->len,
(void *)__iv);
ret = enc ? crypto_skcipher_encrypt(req) :
crypto_skcipher_decrypt(req);
switch (ret) {
case 0:
break;
case -EINPROGRESS:
case -EBUSY:
wait_for_completion(&result.completion);
reinit_completion(&result.completion);
ret = result.err;
if (!ret)
break;
default:
goto out;
}
if (memcmp(__out_buf, __out, tv->len))
ret = -EINVAL;
out:
if (req)
skcipher_request_free(req);
if (__in_buf)
kfree_sensitive(__in_buf);
if (__out_buf)
kfree_sensitive(__out_buf);
return ret;
}
static int test_skcipher(const cipher_test_suite_t *tv,
const char *driver,
u32 type,
u32 mask)
{
struct crypto_skcipher *tfm;
int err = 0;
int i = 0;
const char *algo = NULL;
tfm = crypto_alloc_skcipher(driver, type, mask);
if (IS_ERR(tfm)) {
pr_err("FIPS : skcipher allocation error");
return PTR_ERR(tfm);
}
algo = crypto_tfm_alg_driver_name(crypto_skcipher_tfm(tfm));
for (i = 0; i < tv->tv_count; i++) {
err = __test_skcipher(tfm, FIPS140_TEST_ENCRYPT, &tv->vecs[i]);
if (err) {
pr_err("FIPS : %s, test %d encrypt failed, err=%d\n", algo, i, err);
goto out;
}
}
for (i = 0; i < tv->tv_count; i++) {
err = __test_skcipher(tfm, FIPS140_TEST_DECRYPT, &tv->vecs[i]);
if (err) {
pr_err("FIPS : %s, test %d decrypt failed, err=%d\n", algo, i, err);
goto out;
}
}
pr_err("FIPS : self-tests for %s passed \n", algo);
out:
if (tfm)
crypto_free_skcipher(tfm);
return err;
}
static int __test_aead(struct crypto_aead *tfm,
int enc,
const aead_testvec_t *tv)
{
int ret = -EINVAL;
uint8_t __iv[FIPS140_MAX_LEN_IV] = {0,};
uint8_t __key[FIPS140_MAX_LEN_KEY] = {0,};
uint8_t *__out_buf = NULL;
uint8_t *__in_buf = NULL;
uint8_t *__assoc = NULL;
uint8_t *__temp = NULL;
const uint8_t *__out_ref = NULL;
uint32_t __ilen = 0;
uint32_t __out_ref_len = 0;
uint32_t authsize = 0;
uint32_t iv_len = 0;
uint32_t sg_assoc_idx = 0;
struct aead_request *req = NULL;
struct scatterlist sgin[2];
struct scatterlist sgout[2];
struct tcrypt_result result;
#ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST
const char *algo = crypto_tfm_alg_driver_name(crypto_aead_tfm(tfm));
#endif
__in_buf = kvmalloc(FIPS140_MAX_LEN_PCTEXT, GFP_KERNEL);
__out_buf = kvmalloc(FIPS140_MAX_LEN_PCTEXT, GFP_KERNEL);
__assoc = kvmalloc(FIPS140_MAX_LEN_PCTEXT, GFP_KERNEL);
if (!__in_buf ||
!__out_buf ||
!__assoc) {
goto error_clean_resources;
}
iv_len = crypto_aead_ivsize(tfm);
if (tv->iv_len)
memcpy(__iv, tv->iv, iv_len);
memcpy(__assoc, tv->assoc, tv->alen);
memcpy(__key, tv->key, tv->klen);
// Pass wrong key for functional tests
// Test case : gcm(aes)
#ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST
if (!strcmp(algo, get_fips_functest_mode()))
__key[0] += 1;
#endif
if (enc) {
memcpy(__in_buf, tv->input, tv->ilen);
__ilen = tv->ilen;
__out_ref = tv->result;
__out_ref_len = tv->rlen;
} else {
memcpy(__out_buf, tv->result, tv->rlen);
__temp = __in_buf;
__in_buf = __out_buf;
__out_buf = __temp;
__ilen = tv->rlen;
__out_ref = tv->input;
__out_ref_len = tv->ilen;
}
init_completion(&result.completion);
req = aead_request_alloc(tfm, GFP_KERNEL);
if (!req)
goto error_clean_resources;
aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
crypt_complete, &result);
crypto_aead_clear_flags(tfm, ~0);
ret = crypto_aead_setkey(tfm, __key, tv->klen);
if (ret) {
ret = -EINVAL;
goto error_clean_resources;
}
authsize = abs(__ilen - __out_ref_len);
ret = crypto_aead_setauthsize(tfm, authsize);
if (ret)
goto error_clean_resources;
sg_init_table(sgin, 2);
sg_init_table(sgout, 2);
sg_assoc_idx = !!tv->alen;
sg_set_buf(&sgin[0], __assoc, tv->alen);
sg_set_buf(&sgin[sg_assoc_idx], __in_buf, __ilen);
sg_set_buf(&sgout[0], __assoc, tv->alen);
sg_set_buf(&sgout[sg_assoc_idx], __out_buf, __out_ref_len);
aead_request_set_crypt(req, sgin, sgout, __ilen, __iv);
aead_request_set_ad(req, tv->alen);
ret = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
switch (ret) {
case 0:
break;
case -EINPROGRESS:
case -EBUSY:
wait_for_completion(&result.completion);
reinit_completion(&result.completion);
ret = result.err;
if (!ret)
break;
default:
goto error_clean_resources;
}
if (memcmp(__out_buf, __out_ref, __out_ref_len))
ret = -EINVAL;
error_clean_resources:
if (req)
aead_request_free(req);
if (__in_buf)
kfree_sensitive(__in_buf);
if (__out_buf)
kfree_sensitive(__out_buf);
if (__assoc)
kfree_sensitive(__assoc);
return ret;
}
static int test_aead(const aead_test_suite_t *tv,
const char *driver,
u32 type,
u32 mask)
{
struct crypto_aead *tfm;
int err = 0;
int i = 0;
const char *algo = NULL;
tfm = crypto_alloc_aead(driver, type, mask);
if (IS_ERR(tfm)) {
pr_err("FIPS : aead allocation error");
return PTR_ERR(tfm);
}
algo = crypto_tfm_alg_driver_name(crypto_aead_tfm(tfm));
for (i = 0; i < tv->tv_count; i++) {
err = __test_aead(tfm, FIPS140_TEST_ENCRYPT, &tv->vecs[i]);
if (err) {
pr_err("FIPS : %s, test encrypt %d failed, err=%d\n", algo, i, err);
goto out;
}
}
for (i = 0; i < tv->tv_count; i++) {
err = __test_aead(tfm, FIPS140_TEST_DECRYPT, &tv->vecs[i]);
if (err) {
pr_err("FIPS : %s, test decrypt %d failed, err=%d\n", algo, i, err);
goto out;
}
}
pr_err("FIPS : self-tests for %s passed \n", algo);
out:
if (tfm)
crypto_free_aead(tfm);
return err;
}
static int test_hash(const hash_test_suite_t *tv,
const char *driver,
u32 type,
u32 mask)
{
struct crypto_shash *tfm = NULL;
struct shash_desc *shash_desc = NULL;
int err = 0;
int i = 0;
int size = 0;
uint8_t __digest_buf[FIPS140_MAX_LEN_DIGEST] = {0,};
uint32_t __digest_len = 0;
const char *__ptext = NULL;
size_t __ptext_len = 0;
const char *algo = driver;
#ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST
uint8_t func_buf[1024];
#endif
tfm = crypto_alloc_shash(driver, 0, 0);
if (IS_ERR(tfm)) {
err = -EINVAL;
tfm = NULL;
pr_err("FIPS : shash allocation error");
goto out;
}
algo = crypto_tfm_alg_driver_name(crypto_shash_tfm(tfm));
size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm);
shash_desc = kvmalloc(size, GFP_KERNEL);
if (!shash_desc) {
shash_desc = NULL;
err = -ENOMEM;
goto out;
}
shash_desc->tfm = tfm;
__digest_len = crypto_shash_digestsize(tfm);
for (i = 0; i < tv->tv_count; i++) {
if (tv->vecs[i].klen) {
err = crypto_shash_setkey(tfm, tv->vecs[i].key, tv->vecs[i].klen);
if (err)
goto out;
}
err = crypto_shash_init(shash_desc);
if (err)
goto out;
__ptext = tv->vecs[i].ptext;
__ptext_len = tv->vecs[i].plen;
#ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST
if (!strcmp(algo, get_fips_functest_mode())) {
if (sizeof(func_buf) < tv->vecs[i].plen) {
__ptext_len = sizeof(func_buf);
}
memcpy(func_buf, tv->vecs[i].ptext, __ptext_len);
func_buf[0] = ~func_buf[0];
__ptext = func_buf;
}
#endif
err = crypto_shash_update(shash_desc, __ptext, __ptext_len);
if (err)
goto out;
err = crypto_shash_final(shash_desc, __digest_buf);
if (err)
goto out;
if (memcmp(__digest_buf, tv->vecs[i].digest, __digest_len)) {
err = -EINVAL;
goto out;
}
}
err = 0;
out:
if (err)
pr_err("FIPS : %s, test %d failed, err=%d\n", algo, i, err);
else
pr_err("FIPS : self-tests for %s passed \n", algo);
if (tfm)
crypto_free_shash(tfm);
if (shash_desc)
kfree_sensitive(shash_desc);
return err;
}
static int __test_drbg(const drbg_testvec_t *tv,
int pr,
const char *driver,
u32 type,
u32 mask)
{
int ret = -EAGAIN;
struct crypto_rng *drng;
struct drbg_test_data test_data;
struct drbg_string addtl, pers, testentropy;
unsigned char *buf = kvmalloc(tv->expectedlen, GFP_KERNEL);
if (!buf)
return -ENOMEM;
drng = crypto_alloc_rng(driver, type, mask);
if (IS_ERR(drng)) {
kfree_sensitive(buf);
return -ENOMEM;
}
test_data.testentropy = &testentropy;
// Pass wrong entropy for functional tests
// Test case : drbg
#ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST
if (!strcmp(driver, get_fips_functest_mode())) {
unsigned char temp_buf[FIPS140_MAX_LEN_ENTROPY];
memcpy(temp_buf, tv->entropy, tv->entropylen);
temp_buf[0] += 1;
drbg_string_fill(&testentropy, temp_buf, tv->entropylen);
} else {
drbg_string_fill(&testentropy, tv->entropy, tv->entropylen);
}
#else
drbg_string_fill(&testentropy, tv->entropy, tv->entropylen);
#endif
drbg_string_fill(&pers, tv->pers, tv->perslen);
ret = crypto_drbg_reset_test(drng, &pers, &test_data);
if (ret) {
goto outbuf;
}
drbg_string_fill(&addtl, tv->addtla, tv->addtllen);
if (pr) {
drbg_string_fill(&testentropy, tv->entpra, tv->entprlen);
ret = crypto_drbg_get_bytes_addtl_test(drng,
buf, tv->expectedlen, &addtl, &test_data);
} else {
ret = crypto_drbg_get_bytes_addtl(drng,
buf, tv->expectedlen, &addtl);
}
if (ret < 0)
goto outbuf;
drbg_string_fill(&addtl, tv->addtlb, tv->addtllen);
if (pr) {
drbg_string_fill(&testentropy, tv->entprb, tv->entprlen);
ret = crypto_drbg_get_bytes_addtl_test(drng,
buf, tv->expectedlen, &addtl, &test_data);
} else {
ret = crypto_drbg_get_bytes_addtl(drng,
buf, tv->expectedlen, &addtl);
}
if (ret < 0)
goto outbuf;
ret = memcmp(tv->expected, buf, tv->expectedlen);
outbuf:
if (drng)
crypto_free_rng(drng);
kfree_sensitive(buf);
return ret;
}
static int test_drbg(const drbg_test_suite_t *desc,
const char *driver,
u32 type,
u32 mask)
{
int err = 0;
int pr = 0;
int i = 0;
const drbg_testvec_t *template = desc->vecs;
unsigned int tcount = desc->tv_count;
if (0 == memcmp(driver, "drbg_pr_", 8))
pr = 1;
for (i = 0; i < tcount; i++) {
err = __test_drbg(&template[i], pr, driver, type, mask);
if (err) {
pr_err("FIPS : %s, test %d failed, err=%d\n", driver, i, err);
err = -EINVAL;
break;
}
}
if (!err)
pr_err("FIPS : self-tests for %s passed \n", driver);
return err;
}
int fips140_kat(void)
{
int ret = 0;
#ifdef CONFIG_CRYPTO_AES
ret += test_skcipher(&aes_cbc_tv, "cbc(aes-generic)", 0, 0);
ret += test_skcipher(&aes_ecb_tv, "ecb(aes-generic)", 0, 0);
#ifdef CONFIG_CRYPTO_GCM
ret += test_aead(&aes_gcm_tv, "gcm_base(ctr(aes-generic),ghash-generic)", 0, 0);
#endif
#endif
#ifdef CONFIG_CRYPTO_AES_ARM64_CE
ret += test_skcipher(&aes_ecb_tv, "ecb(aes-ce)", 0, 0);
ret += test_skcipher(&aes_cbc_tv, "cbc(aes-ce)", 0, 0);
#ifdef CONFIG_CRYPTO_GCM
ret += test_aead(&aes_gcm_tv, "gcm_base(ctr(aes-ce),ghash-generic)", 0, 0);
#endif
#endif
#ifdef CONFIG_CRYPTO_SHA1
ret += test_hash(&sha1_tv, "sha1-generic", 0, 0);
ret += test_hash(&hmac_sha1_tv, "hmac(sha1-generic)", 0, 0);
#endif
#ifdef CONFIG_CRYPTO_SHA1_ARM64_CE
ret += test_hash(&sha1_tv, "sha1-ce", 0, 0);
ret += test_hash(&hmac_sha1_tv, "hmac(sha1-ce)", 0, 0);
#endif
#ifdef CONFIG_CRYPTO_SHA256
ret += test_hash(&sha224_tv, "sha224-generic", 0, 0);
ret += test_hash(&sha256_tv, "sha256-generic", 0, 0);
ret += test_hash(&hmac_sha224_tv, "hmac(sha224-generic)", 0, 0);
ret += test_hash(&hmac_sha256_tv, "hmac(sha256-generic)", 0, 0);
#endif
#ifdef CONFIG_CRYPTO_SHA2_ARM64_CE
ret += test_hash(&sha224_tv, "sha224-ce", 0, 0);
ret += test_hash(&sha256_tv, "sha256-ce", 0, 0);
ret += test_hash(&hmac_sha224_tv, "hmac(sha224-ce)", 0, 0);
ret += test_hash(&hmac_sha256_tv, "hmac(sha256-ce)", 0, 0);
#endif
#ifdef CONFIG_CRYPTO_SHA512
ret += test_hash(&sha384_tv, "sha384-generic", 0, 0);
ret += test_hash(&sha512_tv, "sha512-generic", 0, 0);
ret += test_hash(&hmac_sha384_tv, "hmac(sha384-generic)", 0, 0);
ret += test_hash(&hmac_sha512_tv, "hmac(sha512-generic)", 0, 0);
#endif
#if defined CONFIG_CRYPTO_DRBG_HMAC
ret += test_drbg(&drbg_nopr_hmac_sha256_tv, "drbg_nopr_hmac_sha256", 0, 0);
ret += test_drbg(&drbg_pr_hmac_sha256_tv, "drbg_pr_hmac_sha256", 0, 0);
#endif
return ret;
}
// TODO: to be removed
#if defined (__clang__)
#pragma clang optimize on
#elif defined (__GNUC__)
#pragma GCC reset_options
#endif