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