1/* SPDX-License-Identifier: GPL-2.0
2 *
3 * Copyright (C) 2022 Red Hat, Inc.
4 * Author: Vladis Dronov <vdronoff@gmail.com>
5 */
6
7#include <asm/elf.h>
8#include <asm/uaccess.h>
9#include <asm/smp.h>
10#include <crypto/skcipher.h>
11#include <crypto/akcipher.h>
12#include <crypto/acompress.h>
13#include <crypto/rng.h>
14#include <crypto/drbg.h>
15#include <crypto/kpp.h>
16#include <crypto/internal/simd.h>
17#include <crypto/chacha.h>
18#include <crypto/aead.h>
19#include <crypto/hash.h>
20#include <linux/crypto.h>
21#include <linux/debugfs.h>
22#include <linux/delay.h>
23#include <linux/err.h>
24#include <linux/fs.h>
25#include <linux/fips.h>
26#include <linux/kernel.h>
27#include <linux/kthread.h>
28#include <linux/module.h>
29#include <linux/sched.h>
30#include <linux/scatterlist.h>
31#include <linux/time.h>
32#include <linux/vmalloc.h>
33#include <linux/zlib.h>
34#include <linux/once.h>
35#include <linux/random.h>
36#include <linux/slab.h>
37#include <linux/string.h>
38
39static unsigned int data_size __read_mostly = 256;
40static unsigned int debug __read_mostly = 0;
41
42/* tie all skcipher structures together */
43struct skcipher_def {
44	struct scatterlist sginp, sgout;
45	struct crypto_skcipher *tfm;
46	struct skcipher_request *req;
47	struct crypto_wait wait;
48};
49
50/* Perform cipher operations with the chacha lib */
51static int test_lib_chacha(u8 *revert, u8 *cipher, u8 *plain)
52{
53	u32 chacha_state[CHACHA_STATE_WORDS];
54	u8 iv[16], key[32];
55	u64 start, end;
56
57	memset(key, 'X', sizeof(key));
58	memset(iv, 'I', sizeof(iv));
59
60	if (debug) {
61		print_hex_dump(KERN_INFO, "key: ", DUMP_PREFIX_OFFSET,
62			       16, 1, key, 32, 1);
63
64		print_hex_dump(KERN_INFO, "iv:  ", DUMP_PREFIX_OFFSET,
65			       16, 1, iv, 16, 1);
66	}
67
68	/* Encrypt */
69	chacha_init_arch(chacha_state, (u32*)key, iv);
70
71	start = ktime_get_ns();
72	chacha_crypt_arch(chacha_state, cipher, plain, data_size, 20);
73	end = ktime_get_ns();
74
75
76	if (debug)
77		print_hex_dump(KERN_INFO, "encr:", DUMP_PREFIX_OFFSET,
78			       16, 1, cipher,
79			       (data_size > 64 ? 64 : data_size), 1);
80
81	pr_info("lib encryption took: %lld nsec", end - start);
82
83	/* Decrypt */
84	chacha_init_arch(chacha_state, (u32 *)key, iv);
85
86	start = ktime_get_ns();
87	chacha_crypt_arch(chacha_state, revert, cipher, data_size, 20);
88	end = ktime_get_ns();
89
90	if (debug)
91		print_hex_dump(KERN_INFO, "decr:", DUMP_PREFIX_OFFSET,
92			       16, 1, revert,
93			       (data_size > 64 ? 64 : data_size), 1);
94
95	pr_info("lib decryption took: %lld nsec", end - start);
96
97	return 0;
98}
99
100/* Perform cipher operations with skcipher */
101static unsigned int test_skcipher_encdec(struct skcipher_def *sk,
102					 int enc)
103{
104	int rc;
105
106	if (enc) {
107		rc = crypto_wait_req(crypto_skcipher_encrypt(sk->req),
108				     &sk->wait);
109		if (rc)
110			pr_info("skcipher encrypt returned with result"
111				"%d\n", rc);
112	}
113	else
114	{
115		rc = crypto_wait_req(crypto_skcipher_decrypt(sk->req),
116				     &sk->wait);
117		if (rc)
118			pr_info("skcipher decrypt returned with result"
119				"%d\n", rc);
120	}
121
122	return rc;
123}
124
125/* Initialize and trigger cipher operations */
126static int test_skcipher(char *name, u8 *revert, u8 *cipher, u8 *plain)
127{
128	struct skcipher_def sk;
129	struct crypto_skcipher *skcipher = NULL;
130	struct skcipher_request *req = NULL;
131	u8 iv[16], key[32];
132	u64 start, end;
133	int ret = -EFAULT;
134
135	skcipher = crypto_alloc_skcipher(name, 0, 0);
136	if (IS_ERR(skcipher)) {
137		pr_info("could not allocate skcipher %s handle\n", name);
138		return PTR_ERR(skcipher);
139	}
140
141	req = skcipher_request_alloc(skcipher, GFP_KERNEL);
142	if (!req) {
143		pr_info("could not allocate skcipher request\n");
144		ret = -ENOMEM;
145		goto out;
146	}
147
148	skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
149					  crypto_req_done,
150					  &sk.wait);
151
152	memset(key, 'X', sizeof(key));
153	memset(iv, 'I', sizeof(iv));
154
155	if (crypto_skcipher_setkey(skcipher, key, 32)) {
156		pr_info("key could not be set\n");
157		ret = -EAGAIN;
158		goto out;
159	}
160
161	if (debug) {
162		print_hex_dump(KERN_INFO, "key: ", DUMP_PREFIX_OFFSET,
163			       16, 1, key, 32, 1);
164
165		print_hex_dump(KERN_INFO, "iv:  ", DUMP_PREFIX_OFFSET,
166			       16, 1, iv, 16, 1);
167	}
168
169	sk.tfm = skcipher;
170	sk.req = req;
171
172	/* Encrypt in one pass */
173	sg_init_one(&sk.sginp, plain, data_size);
174	sg_init_one(&sk.sgout, cipher, data_size);
175	skcipher_request_set_crypt(req, &sk.sginp, &sk.sgout,
176				   data_size, iv);
177	crypto_init_wait(&sk.wait);
178
179	/* Encrypt data */
180	start = ktime_get_ns();
181	ret = test_skcipher_encdec(&sk, 1);
182	end = ktime_get_ns();
183
184	if (ret)
185		goto out;
186
187	pr_info("%s tfm encryption successful, took %lld nsec\n", name, end - start);
188
189	if (debug)
190		print_hex_dump(KERN_INFO, "encr:", DUMP_PREFIX_OFFSET,
191			       16, 1, cipher,
192			       (data_size > 64 ? 64 : data_size), 1);
193
194	/* Prepare for decryption */
195	memset(iv, 'I', sizeof(iv));
196
197	sg_init_one(&sk.sginp, cipher, data_size);
198	sg_init_one(&sk.sgout, revert, data_size);
199	skcipher_request_set_crypt(req, &sk.sginp, &sk.sgout,
200				   data_size, iv);
201	crypto_init_wait(&sk.wait);
202
203	/* Decrypt data */
204	start = ktime_get_ns();
205	ret = test_skcipher_encdec(&sk, 0);
206	end = ktime_get_ns();
207
208	if (ret)
209		goto out;
210
211	pr_info("%s tfm decryption successful, took %lld nsec\n", name, end - start);
212
213	if (debug)
214		print_hex_dump(KERN_INFO, "decr:", DUMP_PREFIX_OFFSET,
215			       16, 1, revert,
216			       (data_size > 64 ? 64 : data_size), 1);
217
218	/* Dump some internal skcipher data */
219	if (debug)
220		pr_info("skcipher %s: cryptlen %d blksize %d stride %d "
221			"ivsize %d alignmask 0x%x\n",
222			name, sk.req->cryptlen,
223			crypto_skcipher_blocksize(sk.tfm),
224			crypto_skcipher_alg(sk.tfm)->walksize,
225			crypto_skcipher_ivsize(sk.tfm),
226			crypto_skcipher_alignmask(sk.tfm));
227
228out:
229	if (skcipher)
230		crypto_free_skcipher(skcipher);
231	if (req)
232		skcipher_request_free(req);
233	return ret;
234}
235
236static int __init chacha_s390_test_init(void)
237{
238	u8 *plain = NULL, *revert = NULL;
239	u8 *cipher_generic = NULL, *cipher_s390 = NULL;
240	int ret = -1;
241
242	pr_info("s390 ChaCha20 test module: size=%d debug=%d\n",
243		data_size, debug);
244
245	/* Allocate and fill buffers */
246	plain = vmalloc(data_size);
247	if (!plain) {
248		pr_info("could not allocate plain buffer\n");
249		ret = -2;
250		goto out;
251	}
252	memset(plain, 'a', data_size);
253	get_random_bytes(plain, (data_size > 256 ? 256 : data_size));
254
255	cipher_generic = vzalloc(data_size);
256	if (!cipher_generic) {
257		pr_info("could not allocate cipher_generic buffer\n");
258		ret = -2;
259		goto out;
260	}
261
262	cipher_s390 = vzalloc(data_size);
263	if (!cipher_s390) {
264		pr_info("could not allocate cipher_s390 buffer\n");
265		ret = -2;
266		goto out;
267	}
268
269	revert = vzalloc(data_size);
270	if (!revert) {
271		pr_info("could not allocate revert buffer\n");
272		ret = -2;
273		goto out;
274	}
275
276	if (debug)
277		print_hex_dump(KERN_INFO, "src: ", DUMP_PREFIX_OFFSET,
278			       16, 1, plain,
279			       (data_size > 64 ? 64 : data_size), 1);
280
281	/* Use chacha20 generic */
282	ret = test_skcipher("chacha20-generic", revert, cipher_generic, plain);
283	if (ret)
284		goto out;
285
286	if (memcmp(plain, revert, data_size)) {
287		pr_info("generic en/decryption check FAILED\n");
288		ret = -2;
289		goto out;
290	}
291	else
292		pr_info("generic en/decryption check OK\n");
293
294	memset(revert, 0, data_size);
295
296	/* Use chacha20 s390 */
297	ret = test_skcipher("chacha20-s390", revert, cipher_s390, plain);
298	if (ret)
299		goto out;
300
301	if (memcmp(plain, revert, data_size)) {
302		pr_info("s390 en/decryption check FAILED\n");
303		ret = -2;
304		goto out;
305	}
306	else
307		pr_info("s390 en/decryption check OK\n");
308
309	if (memcmp(cipher_generic, cipher_s390, data_size)) {
310		pr_info("s390 vs generic check FAILED\n");
311		ret = -2;
312		goto out;
313	}
314	else
315		pr_info("s390 vs generic check OK\n");
316
317	memset(cipher_s390, 0, data_size);
318	memset(revert, 0, data_size);
319
320	/* Use chacha20 lib */
321	test_lib_chacha(revert, cipher_s390, plain);
322
323	if (memcmp(plain, revert, data_size)) {
324		pr_info("lib en/decryption check FAILED\n");
325		ret = -2;
326		goto out;
327	}
328	else
329		pr_info("lib en/decryption check OK\n");
330
331	if (memcmp(cipher_generic, cipher_s390, data_size)) {
332		pr_info("lib vs generic check FAILED\n");
333		ret = -2;
334		goto out;
335	}
336	else
337		pr_info("lib vs generic check OK\n");
338
339	pr_info("--- chacha20 s390 test end ---\n");
340
341out:
342	if (plain)
343		vfree(plain);
344	if (cipher_generic)
345		vfree(cipher_generic);
346	if (cipher_s390)
347		vfree(cipher_s390);
348	if (revert)
349		vfree(revert);
350
351	return -1;
352}
353
354static void __exit chacha_s390_test_exit(void)
355{
356	pr_info("s390 ChaCha20 test module exit\n");
357}
358
359module_param_named(size, data_size, uint, 0660);
360module_param(debug, int, 0660);
361MODULE_PARM_DESC(size, "Size of a plaintext");
362MODULE_PARM_DESC(debug, "Debug level (0=off,1=on)");
363
364module_init(chacha_s390_test_init);
365module_exit(chacha_s390_test_exit);
366
367MODULE_DESCRIPTION("s390 ChaCha20 self-test");
368MODULE_AUTHOR("Vladis Dronov <vdronoff@gmail.com>");
369MODULE_LICENSE("GPL v2");
370