162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * Copyright (C) 2022 Red Hat, Inc.
462306a36Sopenharmony_ci * Author: Vladis Dronov <vdronoff@gmail.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <asm/elf.h>
862306a36Sopenharmony_ci#include <asm/uaccess.h>
962306a36Sopenharmony_ci#include <asm/smp.h>
1062306a36Sopenharmony_ci#include <crypto/skcipher.h>
1162306a36Sopenharmony_ci#include <crypto/akcipher.h>
1262306a36Sopenharmony_ci#include <crypto/acompress.h>
1362306a36Sopenharmony_ci#include <crypto/rng.h>
1462306a36Sopenharmony_ci#include <crypto/drbg.h>
1562306a36Sopenharmony_ci#include <crypto/kpp.h>
1662306a36Sopenharmony_ci#include <crypto/internal/simd.h>
1762306a36Sopenharmony_ci#include <crypto/chacha.h>
1862306a36Sopenharmony_ci#include <crypto/aead.h>
1962306a36Sopenharmony_ci#include <crypto/hash.h>
2062306a36Sopenharmony_ci#include <linux/crypto.h>
2162306a36Sopenharmony_ci#include <linux/debugfs.h>
2262306a36Sopenharmony_ci#include <linux/delay.h>
2362306a36Sopenharmony_ci#include <linux/err.h>
2462306a36Sopenharmony_ci#include <linux/fs.h>
2562306a36Sopenharmony_ci#include <linux/fips.h>
2662306a36Sopenharmony_ci#include <linux/kernel.h>
2762306a36Sopenharmony_ci#include <linux/kthread.h>
2862306a36Sopenharmony_ci#include <linux/module.h>
2962306a36Sopenharmony_ci#include <linux/sched.h>
3062306a36Sopenharmony_ci#include <linux/scatterlist.h>
3162306a36Sopenharmony_ci#include <linux/time.h>
3262306a36Sopenharmony_ci#include <linux/vmalloc.h>
3362306a36Sopenharmony_ci#include <linux/zlib.h>
3462306a36Sopenharmony_ci#include <linux/once.h>
3562306a36Sopenharmony_ci#include <linux/random.h>
3662306a36Sopenharmony_ci#include <linux/slab.h>
3762306a36Sopenharmony_ci#include <linux/string.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic unsigned int data_size __read_mostly = 256;
4062306a36Sopenharmony_cistatic unsigned int debug __read_mostly = 0;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* tie all skcipher structures together */
4362306a36Sopenharmony_cistruct skcipher_def {
4462306a36Sopenharmony_ci	struct scatterlist sginp, sgout;
4562306a36Sopenharmony_ci	struct crypto_skcipher *tfm;
4662306a36Sopenharmony_ci	struct skcipher_request *req;
4762306a36Sopenharmony_ci	struct crypto_wait wait;
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* Perform cipher operations with the chacha lib */
5162306a36Sopenharmony_cistatic int test_lib_chacha(u8 *revert, u8 *cipher, u8 *plain)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	u32 chacha_state[CHACHA_STATE_WORDS];
5462306a36Sopenharmony_ci	u8 iv[16], key[32];
5562306a36Sopenharmony_ci	u64 start, end;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	memset(key, 'X', sizeof(key));
5862306a36Sopenharmony_ci	memset(iv, 'I', sizeof(iv));
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (debug) {
6162306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, "key: ", DUMP_PREFIX_OFFSET,
6262306a36Sopenharmony_ci			       16, 1, key, 32, 1);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, "iv:  ", DUMP_PREFIX_OFFSET,
6562306a36Sopenharmony_ci			       16, 1, iv, 16, 1);
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* Encrypt */
6962306a36Sopenharmony_ci	chacha_init_arch(chacha_state, (u32*)key, iv);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	start = ktime_get_ns();
7262306a36Sopenharmony_ci	chacha_crypt_arch(chacha_state, cipher, plain, data_size, 20);
7362306a36Sopenharmony_ci	end = ktime_get_ns();
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (debug)
7762306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, "encr:", DUMP_PREFIX_OFFSET,
7862306a36Sopenharmony_ci			       16, 1, cipher,
7962306a36Sopenharmony_ci			       (data_size > 64 ? 64 : data_size), 1);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	pr_info("lib encryption took: %lld nsec", end - start);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/* Decrypt */
8462306a36Sopenharmony_ci	chacha_init_arch(chacha_state, (u32 *)key, iv);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	start = ktime_get_ns();
8762306a36Sopenharmony_ci	chacha_crypt_arch(chacha_state, revert, cipher, data_size, 20);
8862306a36Sopenharmony_ci	end = ktime_get_ns();
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (debug)
9162306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, "decr:", DUMP_PREFIX_OFFSET,
9262306a36Sopenharmony_ci			       16, 1, revert,
9362306a36Sopenharmony_ci			       (data_size > 64 ? 64 : data_size), 1);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	pr_info("lib decryption took: %lld nsec", end - start);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/* Perform cipher operations with skcipher */
10162306a36Sopenharmony_cistatic unsigned int test_skcipher_encdec(struct skcipher_def *sk,
10262306a36Sopenharmony_ci					 int enc)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	int rc;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (enc) {
10762306a36Sopenharmony_ci		rc = crypto_wait_req(crypto_skcipher_encrypt(sk->req),
10862306a36Sopenharmony_ci				     &sk->wait);
10962306a36Sopenharmony_ci		if (rc)
11062306a36Sopenharmony_ci			pr_info("skcipher encrypt returned with result"
11162306a36Sopenharmony_ci				"%d\n", rc);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci	else
11462306a36Sopenharmony_ci	{
11562306a36Sopenharmony_ci		rc = crypto_wait_req(crypto_skcipher_decrypt(sk->req),
11662306a36Sopenharmony_ci				     &sk->wait);
11762306a36Sopenharmony_ci		if (rc)
11862306a36Sopenharmony_ci			pr_info("skcipher decrypt returned with result"
11962306a36Sopenharmony_ci				"%d\n", rc);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return rc;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/* Initialize and trigger cipher operations */
12662306a36Sopenharmony_cistatic int test_skcipher(char *name, u8 *revert, u8 *cipher, u8 *plain)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct skcipher_def sk;
12962306a36Sopenharmony_ci	struct crypto_skcipher *skcipher = NULL;
13062306a36Sopenharmony_ci	struct skcipher_request *req = NULL;
13162306a36Sopenharmony_ci	u8 iv[16], key[32];
13262306a36Sopenharmony_ci	u64 start, end;
13362306a36Sopenharmony_ci	int ret = -EFAULT;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	skcipher = crypto_alloc_skcipher(name, 0, 0);
13662306a36Sopenharmony_ci	if (IS_ERR(skcipher)) {
13762306a36Sopenharmony_ci		pr_info("could not allocate skcipher %s handle\n", name);
13862306a36Sopenharmony_ci		return PTR_ERR(skcipher);
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	req = skcipher_request_alloc(skcipher, GFP_KERNEL);
14262306a36Sopenharmony_ci	if (!req) {
14362306a36Sopenharmony_ci		pr_info("could not allocate skcipher request\n");
14462306a36Sopenharmony_ci		ret = -ENOMEM;
14562306a36Sopenharmony_ci		goto out;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
14962306a36Sopenharmony_ci					  crypto_req_done,
15062306a36Sopenharmony_ci					  &sk.wait);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	memset(key, 'X', sizeof(key));
15362306a36Sopenharmony_ci	memset(iv, 'I', sizeof(iv));
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (crypto_skcipher_setkey(skcipher, key, 32)) {
15662306a36Sopenharmony_ci		pr_info("key could not be set\n");
15762306a36Sopenharmony_ci		ret = -EAGAIN;
15862306a36Sopenharmony_ci		goto out;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (debug) {
16262306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, "key: ", DUMP_PREFIX_OFFSET,
16362306a36Sopenharmony_ci			       16, 1, key, 32, 1);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, "iv:  ", DUMP_PREFIX_OFFSET,
16662306a36Sopenharmony_ci			       16, 1, iv, 16, 1);
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	sk.tfm = skcipher;
17062306a36Sopenharmony_ci	sk.req = req;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Encrypt in one pass */
17362306a36Sopenharmony_ci	sg_init_one(&sk.sginp, plain, data_size);
17462306a36Sopenharmony_ci	sg_init_one(&sk.sgout, cipher, data_size);
17562306a36Sopenharmony_ci	skcipher_request_set_crypt(req, &sk.sginp, &sk.sgout,
17662306a36Sopenharmony_ci				   data_size, iv);
17762306a36Sopenharmony_ci	crypto_init_wait(&sk.wait);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* Encrypt data */
18062306a36Sopenharmony_ci	start = ktime_get_ns();
18162306a36Sopenharmony_ci	ret = test_skcipher_encdec(&sk, 1);
18262306a36Sopenharmony_ci	end = ktime_get_ns();
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (ret)
18562306a36Sopenharmony_ci		goto out;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	pr_info("%s tfm encryption successful, took %lld nsec\n", name, end - start);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (debug)
19062306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, "encr:", DUMP_PREFIX_OFFSET,
19162306a36Sopenharmony_ci			       16, 1, cipher,
19262306a36Sopenharmony_ci			       (data_size > 64 ? 64 : data_size), 1);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* Prepare for decryption */
19562306a36Sopenharmony_ci	memset(iv, 'I', sizeof(iv));
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	sg_init_one(&sk.sginp, cipher, data_size);
19862306a36Sopenharmony_ci	sg_init_one(&sk.sgout, revert, data_size);
19962306a36Sopenharmony_ci	skcipher_request_set_crypt(req, &sk.sginp, &sk.sgout,
20062306a36Sopenharmony_ci				   data_size, iv);
20162306a36Sopenharmony_ci	crypto_init_wait(&sk.wait);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Decrypt data */
20462306a36Sopenharmony_ci	start = ktime_get_ns();
20562306a36Sopenharmony_ci	ret = test_skcipher_encdec(&sk, 0);
20662306a36Sopenharmony_ci	end = ktime_get_ns();
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (ret)
20962306a36Sopenharmony_ci		goto out;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	pr_info("%s tfm decryption successful, took %lld nsec\n", name, end - start);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (debug)
21462306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, "decr:", DUMP_PREFIX_OFFSET,
21562306a36Sopenharmony_ci			       16, 1, revert,
21662306a36Sopenharmony_ci			       (data_size > 64 ? 64 : data_size), 1);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* Dump some internal skcipher data */
21962306a36Sopenharmony_ci	if (debug)
22062306a36Sopenharmony_ci		pr_info("skcipher %s: cryptlen %d blksize %d stride %d "
22162306a36Sopenharmony_ci			"ivsize %d alignmask 0x%x\n",
22262306a36Sopenharmony_ci			name, sk.req->cryptlen,
22362306a36Sopenharmony_ci			crypto_skcipher_blocksize(sk.tfm),
22462306a36Sopenharmony_ci			crypto_skcipher_alg(sk.tfm)->walksize,
22562306a36Sopenharmony_ci			crypto_skcipher_ivsize(sk.tfm),
22662306a36Sopenharmony_ci			crypto_skcipher_alignmask(sk.tfm));
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ciout:
22962306a36Sopenharmony_ci	if (skcipher)
23062306a36Sopenharmony_ci		crypto_free_skcipher(skcipher);
23162306a36Sopenharmony_ci	if (req)
23262306a36Sopenharmony_ci		skcipher_request_free(req);
23362306a36Sopenharmony_ci	return ret;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int __init chacha_s390_test_init(void)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	u8 *plain = NULL, *revert = NULL;
23962306a36Sopenharmony_ci	u8 *cipher_generic = NULL, *cipher_s390 = NULL;
24062306a36Sopenharmony_ci	int ret = -1;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	pr_info("s390 ChaCha20 test module: size=%d debug=%d\n",
24362306a36Sopenharmony_ci		data_size, debug);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Allocate and fill buffers */
24662306a36Sopenharmony_ci	plain = vmalloc(data_size);
24762306a36Sopenharmony_ci	if (!plain) {
24862306a36Sopenharmony_ci		pr_info("could not allocate plain buffer\n");
24962306a36Sopenharmony_ci		ret = -2;
25062306a36Sopenharmony_ci		goto out;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci	memset(plain, 'a', data_size);
25362306a36Sopenharmony_ci	get_random_bytes(plain, (data_size > 256 ? 256 : data_size));
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	cipher_generic = vzalloc(data_size);
25662306a36Sopenharmony_ci	if (!cipher_generic) {
25762306a36Sopenharmony_ci		pr_info("could not allocate cipher_generic buffer\n");
25862306a36Sopenharmony_ci		ret = -2;
25962306a36Sopenharmony_ci		goto out;
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	cipher_s390 = vzalloc(data_size);
26362306a36Sopenharmony_ci	if (!cipher_s390) {
26462306a36Sopenharmony_ci		pr_info("could not allocate cipher_s390 buffer\n");
26562306a36Sopenharmony_ci		ret = -2;
26662306a36Sopenharmony_ci		goto out;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	revert = vzalloc(data_size);
27062306a36Sopenharmony_ci	if (!revert) {
27162306a36Sopenharmony_ci		pr_info("could not allocate revert buffer\n");
27262306a36Sopenharmony_ci		ret = -2;
27362306a36Sopenharmony_ci		goto out;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (debug)
27762306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, "src: ", DUMP_PREFIX_OFFSET,
27862306a36Sopenharmony_ci			       16, 1, plain,
27962306a36Sopenharmony_ci			       (data_size > 64 ? 64 : data_size), 1);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* Use chacha20 generic */
28262306a36Sopenharmony_ci	ret = test_skcipher("chacha20-generic", revert, cipher_generic, plain);
28362306a36Sopenharmony_ci	if (ret)
28462306a36Sopenharmony_ci		goto out;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (memcmp(plain, revert, data_size)) {
28762306a36Sopenharmony_ci		pr_info("generic en/decryption check FAILED\n");
28862306a36Sopenharmony_ci		ret = -2;
28962306a36Sopenharmony_ci		goto out;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci	else
29262306a36Sopenharmony_ci		pr_info("generic en/decryption check OK\n");
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	memset(revert, 0, data_size);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* Use chacha20 s390 */
29762306a36Sopenharmony_ci	ret = test_skcipher("chacha20-s390", revert, cipher_s390, plain);
29862306a36Sopenharmony_ci	if (ret)
29962306a36Sopenharmony_ci		goto out;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (memcmp(plain, revert, data_size)) {
30262306a36Sopenharmony_ci		pr_info("s390 en/decryption check FAILED\n");
30362306a36Sopenharmony_ci		ret = -2;
30462306a36Sopenharmony_ci		goto out;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci	else
30762306a36Sopenharmony_ci		pr_info("s390 en/decryption check OK\n");
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (memcmp(cipher_generic, cipher_s390, data_size)) {
31062306a36Sopenharmony_ci		pr_info("s390 vs generic check FAILED\n");
31162306a36Sopenharmony_ci		ret = -2;
31262306a36Sopenharmony_ci		goto out;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci	else
31562306a36Sopenharmony_ci		pr_info("s390 vs generic check OK\n");
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	memset(cipher_s390, 0, data_size);
31862306a36Sopenharmony_ci	memset(revert, 0, data_size);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* Use chacha20 lib */
32162306a36Sopenharmony_ci	test_lib_chacha(revert, cipher_s390, plain);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (memcmp(plain, revert, data_size)) {
32462306a36Sopenharmony_ci		pr_info("lib en/decryption check FAILED\n");
32562306a36Sopenharmony_ci		ret = -2;
32662306a36Sopenharmony_ci		goto out;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	else
32962306a36Sopenharmony_ci		pr_info("lib en/decryption check OK\n");
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (memcmp(cipher_generic, cipher_s390, data_size)) {
33262306a36Sopenharmony_ci		pr_info("lib vs generic check FAILED\n");
33362306a36Sopenharmony_ci		ret = -2;
33462306a36Sopenharmony_ci		goto out;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci	else
33762306a36Sopenharmony_ci		pr_info("lib vs generic check OK\n");
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	pr_info("--- chacha20 s390 test end ---\n");
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ciout:
34262306a36Sopenharmony_ci	if (plain)
34362306a36Sopenharmony_ci		vfree(plain);
34462306a36Sopenharmony_ci	if (cipher_generic)
34562306a36Sopenharmony_ci		vfree(cipher_generic);
34662306a36Sopenharmony_ci	if (cipher_s390)
34762306a36Sopenharmony_ci		vfree(cipher_s390);
34862306a36Sopenharmony_ci	if (revert)
34962306a36Sopenharmony_ci		vfree(revert);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return -1;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic void __exit chacha_s390_test_exit(void)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	pr_info("s390 ChaCha20 test module exit\n");
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cimodule_param_named(size, data_size, uint, 0660);
36062306a36Sopenharmony_cimodule_param(debug, int, 0660);
36162306a36Sopenharmony_ciMODULE_PARM_DESC(size, "Size of a plaintext");
36262306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0=off,1=on)");
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cimodule_init(chacha_s390_test_init);
36562306a36Sopenharmony_cimodule_exit(chacha_s390_test_exit);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ciMODULE_DESCRIPTION("s390 ChaCha20 self-test");
36862306a36Sopenharmony_ciMODULE_AUTHOR("Vladis Dronov <vdronoff@gmail.com>");
36962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
370