#include #include #include #include #include #include #include #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; extern const kbkdf_test_suite_t kbkdf_hmac_sha512_ctr_nist_tv; // TODO: to be removed #pragma clang optimize off #ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST static char *fips_functest_mode; static char *fips_functest_KAT_list[] = { "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", "kbkdf_hmac_sha512_ctr", "integrity" }; static char *fips_functest_conditional_list[] = { "ndrng_crngt", "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 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 = NULL; const uint8_t* __in = NULL; const uint8_t* __out = NULL; __out_buf = kmalloc(FIPS140_MAX_LEN_PCTEXT, GFP_KERNEL); __in_buf = kmalloc(FIPS140_MAX_LEN_PCTEXT, GFP_KERNEL); __iv = kmalloc(FIPS140_MAX_LEN_IV, GFP_KERNEL); if ((!__out_buf) || (!__in_buf) || (!__iv)) { 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: skcipher_request_free(req); kzfree(__in_buf); kzfree(__out_buf); kzfree(__iv); 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: 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 = NULL; uint8_t *__key = NULL; 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; struct scatterlist *sgin; struct scatterlist *sgout; struct crypto_wait wait; #ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST const char *algo = crypto_tfm_alg_driver_name(crypto_aead_tfm(tfm)); #endif __iv = kzalloc(FIPS140_MAX_LEN_IV, GFP_KERNEL); __key = kzalloc(FIPS140_MAX_LEN_KEY, GFP_KERNEL); __in_buf = kzalloc(3*FIPS140_MAX_LEN_PCTEXT, GFP_KERNEL); __out_buf = kzalloc(3*FIPS140_MAX_LEN_PCTEXT, GFP_KERNEL); __assoc = kzalloc(FIPS140_MAX_LEN_PCTEXT, GFP_KERNEL); sgin = kzalloc(2*sizeof(*sgin), GFP_KERNEL); sgout = kzalloc(2*sizeof(*sgout), GFP_KERNEL); if (!__iv || !__key || !__in_buf || !__out_buf || !__assoc || !sgin || !sgout) { 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; } crypto_init_wait(&wait); req = aead_request_alloc(tfm, GFP_KERNEL); if (!req) goto error_clean_resources; aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait); 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 = crypto_wait_req(enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req), &wait); switch (ret) { case 0: break; default: goto error_clean_resources; } if (memcmp(__out_buf, __out_ref, __out_ref_len)) ret = -EINVAL; error_clean_resources: kzfree(sgin); kzfree(sgout); aead_request_free(req); kzfree(__iv); kzfree(__key); kzfree(__in_buf); kzfree(__out_buf); kzfree(__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: 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 = NULL; uint32_t __digest_len = 0; const char* __ptext = NULL; const char *algo = driver; __digest_buf = kmalloc(FIPS140_MAX_LEN_DIGEST, GFP_KERNEL); if(!__digest_buf) { return -ENOMEM; } 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 = kmalloc(size, GFP_KERNEL); if (!shash_desc) { shash_desc = NULL; err = -ENOMEM; goto out; } shash_desc->tfm = tfm; shash_desc->flags = 0x0; __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; #ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST if (!strcmp(algo, get_fips_functest_mode())) { uint8_t func_buf[1024]; uint32_t func_buf_len = sizeof(func_buf); if (sizeof(func_buf) < tv->vecs[i].plen) func_buf_len = sizeof(func_buf); else func_buf_len = tv->vecs[i].plen; memcpy(func_buf, tv->vecs[i].ptext, func_buf_len); func_buf[0] = func_buf[0] + 1; __ptext = func_buf; } #endif err = crypto_shash_update(shash_desc, __ptext, tv->vecs[i].plen); 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) kzfree(shash_desc); if(__digest_buf) kzfree(__digest_buf); 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 = kzalloc(tv->expectedlen, GFP_KERNEL); if (!buf) return -ENOMEM; drng = crypto_alloc_rng(driver, type, mask); if (IS_ERR(drng)) { kzfree(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: crypto_free_rng(drng); kzfree(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 test_kbkdf(const kbkdf_test_suite_t *tv) { int ret = 0; uint32_t i = 0; uint32_t L_calc = 0; size_t ki_length = 0; uint8_t Ko_calc[MAX_KDF_OUTPUT_LENGTH_BYTES] = {0}; for(i = 0; i < tv->tv_count; i++) { L_calc = tv->vecs[i].L; ki_length = tv->vecs[i].KiLength; #ifdef CONFIG_CRYPTO_FIPS_FUNC_TEST if (!strcmp("kbkdf_hmac_sha512_ctr", get_fips_functest_mode())) ki_length = 1; #endif ret = crypto_calc_kdf_hmac_sha512_ctr( tv->vecs[i].mode, tv->vecs[i].rlen, tv->vecs[i].Ki, ki_length, Ko_calc, &L_calc, tv->vecs[i].Label, tv->vecs[i].LabelLength, tv->vecs[i].Context, tv->vecs[i].ContextLength); if (ret) { pr_err("FIPS : kbkdf_hmac_sha512_ctr, test %d internal error, err=%d\n", i, ret); return ret; } if (memcmp(tv->vecs[i].Ko, Ko_calc, tv->vecs[i].L)) { pr_err("FIPS : kbkdf_hmac_sha512_ctr, test %d failed \n", i); return -EINVAL; } } pr_err("FIPS : self-tests for kbkdf_hmac_sha512_ctr passed \n"); return ret; } 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 #ifdef CONFIG_CRYPTO_KBKDF_CTR_HMAC_SHA512 ret += test_kbkdf(&kbkdf_hmac_sha512_ctr_nist_tv); #endif return ret; } // TODO: to be removed #pragma clang optimize on