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 
39 static unsigned int data_size __read_mostly = 256;
40 static unsigned int debug __read_mostly = 0;
41 
42 /* tie all skcipher structures together */
43 struct 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 */
test_lib_chacha(u8 * revert,u8 * cipher,u8 * plain)51 static 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 */
test_skcipher_encdec(struct skcipher_def * sk,int enc)101 static 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 */
test_skcipher(char * name,u8 * revert,u8 * cipher,u8 * plain)126 static 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 
228 out:
229 	if (skcipher)
230 		crypto_free_skcipher(skcipher);
231 	if (req)
232 		skcipher_request_free(req);
233 	return ret;
234 }
235 
chacha_s390_test_init(void)236 static 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 
341 out:
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 
chacha_s390_test_exit(void)354 static void __exit chacha_s390_test_exit(void)
355 {
356 	pr_info("s390 ChaCha20 test module exit\n");
357 }
358 
359 module_param_named(size, data_size, uint, 0660);
360 module_param(debug, int, 0660);
361 MODULE_PARM_DESC(size, "Size of a plaintext");
362 MODULE_PARM_DESC(debug, "Debug level (0=off,1=on)");
363 
364 module_init(chacha_s390_test_init);
365 module_exit(chacha_s390_test_exit);
366 
367 MODULE_DESCRIPTION("s390 ChaCha20 self-test");
368 MODULE_AUTHOR("Vladis Dronov <vdronoff@gmail.com>");
369 MODULE_LICENSE("GPL v2");
370