Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

crypto: s390 - add crypto library interface for ChaCha20

Implement a crypto library interface for the s390-native ChaCha20 cipher
algorithm. This allows us to stop to select CRYPTO_CHACHA20 and instead
select CRYPTO_ARCH_HAVE_LIB_CHACHA. This allows BIG_KEYS=y not to build
a whole ChaCha20 crypto infrastructure as a built-in, but build a smaller
CRYPTO_LIB_CHACHA instead.

Make CRYPTO_CHACHA_S390 config entry to look like similar ones on other
architectures. Remove CRYPTO_ALGAPI select as anyway it is selected by
CRYPTO_SKCIPHER.

Add a new test module and a test script for ChaCha20 cipher and its
interfaces. Here are test results on an idle z15 machine:

Data | Generic crypto TFM | s390 crypto TFM | s390 lib
size | enc dec | enc dec | enc dec
-----+--------------------+------------------+----------------
512b | 1545ns 1295ns | 604ns 446ns | 430ns 407ns
4k | 9536ns 9463ns | 2329ns 2174ns | 2170ns 2154ns
64k | 149.6us 149.3us | 34.4us 34.5us | 33.9us 33.1us
6M | 23.61ms 23.11ms | 4223us 4160us | 3951us 4008us
60M | 143.9ms 143.9ms | 33.5ms 33.2ms | 32.2ms 32.1ms

Signed-off-by: Vladis Dronov <vdronov@redhat.com>
Reviewed-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

Vladis Dronov and committed by
Herbert Xu
349d03ff 6ae7a8b1

+452 -4
+32 -2
arch/s390/crypto/chacha-glue.c
··· 62 62 return rc; 63 63 } 64 64 65 + void hchacha_block_arch(const u32 *state, u32 *stream, int nrounds) 66 + { 67 + /* TODO: implement hchacha_block_arch() in assembly */ 68 + hchacha_block_generic(state, stream, nrounds); 69 + } 70 + EXPORT_SYMBOL(hchacha_block_arch); 71 + 72 + void chacha_init_arch(u32 *state, const u32 *key, const u8 *iv) 73 + { 74 + chacha_init_generic(state, key, iv); 75 + } 76 + EXPORT_SYMBOL(chacha_init_arch); 77 + 78 + void chacha_crypt_arch(u32 *state, u8 *dst, const u8 *src, 79 + unsigned int bytes, int nrounds) 80 + { 81 + /* s390 chacha20 implementation has 20 rounds hard-coded, 82 + * it cannot handle a block of data or less, but otherwise 83 + * it can handle data of arbitrary size 84 + */ 85 + if (bytes <= CHACHA_BLOCK_SIZE || nrounds != 20) 86 + chacha_crypt_generic(state, dst, src, bytes, nrounds); 87 + else 88 + chacha20_crypt_s390(state, dst, src, bytes, 89 + &state[4], &state[12]); 90 + } 91 + EXPORT_SYMBOL(chacha_crypt_arch); 92 + 65 93 static struct skcipher_alg chacha_algs[] = { 66 94 { 67 95 .base.cra_name = "chacha20", ··· 111 83 112 84 static int __init chacha_mod_init(void) 113 85 { 114 - return crypto_register_skciphers(chacha_algs, ARRAY_SIZE(chacha_algs)); 86 + return IS_REACHABLE(CONFIG_CRYPTO_SKCIPHER) ? 87 + crypto_register_skciphers(chacha_algs, ARRAY_SIZE(chacha_algs)) : 0; 115 88 } 116 89 117 90 static void __exit chacha_mod_fini(void) 118 91 { 119 - crypto_unregister_skciphers(chacha_algs, ARRAY_SIZE(chacha_algs)); 92 + if (IS_REACHABLE(CONFIG_CRYPTO_SKCIPHER)) 93 + crypto_unregister_skciphers(chacha_algs, ARRAY_SIZE(chacha_algs)); 120 94 } 121 95 122 96 module_cpu_feature_match(VXRS, chacha_mod_init);
+2 -2
drivers/crypto/Kconfig
··· 216 216 config CRYPTO_CHACHA_S390 217 217 tristate "ChaCha20 stream cipher" 218 218 depends on S390 219 - select CRYPTO_ALGAPI 220 219 select CRYPTO_SKCIPHER 221 - select CRYPTO_CHACHA20 220 + select CRYPTO_LIB_CHACHA_GENERIC 221 + select CRYPTO_ARCH_HAVE_LIB_CHACHA 222 222 help 223 223 This is the s390 SIMD implementation of the ChaCha20 stream 224 224 cipher (RFC 7539).
+12
tools/testing/crypto/chacha20-s390/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # Copyright (C) 2022 Red Hat, Inc. 4 + # Author: Vladis Dronov <vdronoff@gmail.com> 5 + 6 + obj-m += test_cipher.o 7 + test_cipher-y := test-cipher.o 8 + 9 + all: 10 + make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules 11 + clean: 12 + make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
+34
tools/testing/crypto/chacha20-s390/run-tests.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Copyright (C) 2022 Red Hat, Inc. 5 + # Author: Vladis Dronov <vdronoff@gmail.com> 6 + # 7 + # This script runs (via instmod) test-cipher.ko module which invokes 8 + # generic and s390-native ChaCha20 encryprion algorithms with different 9 + # size of data. Check 'dmesg' for results. 10 + # 11 + # The insmod error is expected: 12 + # insmod: ERROR: could not insert module test_cipher.ko: Operation not permitted 13 + 14 + lsmod | grep chacha | cut -f1 -d' ' | xargs rmmod 15 + modprobe chacha_generic 16 + modprobe chacha_s390 17 + 18 + # run encryption for different data size, including whole block(s) +/- 1 19 + insmod test_cipher.ko size=63 20 + insmod test_cipher.ko size=64 21 + insmod test_cipher.ko size=65 22 + insmod test_cipher.ko size=127 23 + insmod test_cipher.ko size=128 24 + insmod test_cipher.ko size=129 25 + insmod test_cipher.ko size=511 26 + insmod test_cipher.ko size=512 27 + insmod test_cipher.ko size=513 28 + insmod test_cipher.ko size=4096 29 + insmod test_cipher.ko size=65611 30 + insmod test_cipher.ko size=6291456 31 + insmod test_cipher.ko size=62914560 32 + 33 + # print test logs 34 + dmesg | tail -170
+372
tools/testing/crypto/chacha20-s390/test-cipher.c
··· 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 */ 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 */ 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 */ 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 + 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 = vmalloc(data_size); 256 + if (!cipher_generic) { 257 + pr_info("could not allocate cipher_generic buffer\n"); 258 + ret = -2; 259 + goto out; 260 + } 261 + memset(cipher_generic, 0, data_size); 262 + 263 + cipher_s390 = vmalloc(data_size); 264 + if (!cipher_s390) { 265 + pr_info("could not allocate cipher_s390 buffer\n"); 266 + ret = -2; 267 + goto out; 268 + } 269 + memset(cipher_s390, 0, data_size); 270 + 271 + revert = vmalloc(data_size); 272 + if (!revert) { 273 + pr_info("could not allocate revert buffer\n"); 274 + ret = -2; 275 + goto out; 276 + } 277 + memset(revert, 0, data_size); 278 + 279 + if (debug) 280 + print_hex_dump(KERN_INFO, "src: ", DUMP_PREFIX_OFFSET, 281 + 16, 1, plain, 282 + (data_size > 64 ? 64 : data_size), 1); 283 + 284 + /* Use chacha20 generic */ 285 + ret = test_skcipher("chacha20-generic", revert, cipher_generic, plain); 286 + if (ret) 287 + goto out; 288 + 289 + if (memcmp(plain, revert, data_size)) { 290 + pr_info("generic en/decryption check FAILED\n"); 291 + ret = -2; 292 + goto out; 293 + } 294 + else 295 + pr_info("generic en/decryption check OK\n"); 296 + 297 + memset(revert, 0, data_size); 298 + 299 + /* Use chacha20 s390 */ 300 + ret = test_skcipher("chacha20-s390", revert, cipher_s390, plain); 301 + if (ret) 302 + goto out; 303 + 304 + if (memcmp(plain, revert, data_size)) { 305 + pr_info("s390 en/decryption check FAILED\n"); 306 + ret = -2; 307 + goto out; 308 + } 309 + else 310 + pr_info("s390 en/decryption check OK\n"); 311 + 312 + if (memcmp(cipher_generic, cipher_s390, data_size)) { 313 + pr_info("s390 vs generic check FAILED\n"); 314 + ret = -2; 315 + goto out; 316 + } 317 + else 318 + pr_info("s390 vs generic check OK\n"); 319 + 320 + memset(cipher_s390, 0, data_size); 321 + memset(revert, 0, data_size); 322 + 323 + /* Use chacha20 lib */ 324 + test_lib_chacha(revert, cipher_s390, plain); 325 + 326 + if (memcmp(plain, revert, data_size)) { 327 + pr_info("lib en/decryption check FAILED\n"); 328 + ret = -2; 329 + goto out; 330 + } 331 + else 332 + pr_info("lib en/decryption check OK\n"); 333 + 334 + if (memcmp(cipher_generic, cipher_s390, data_size)) { 335 + pr_info("lib vs generic check FAILED\n"); 336 + ret = -2; 337 + goto out; 338 + } 339 + else 340 + pr_info("lib vs generic check OK\n"); 341 + 342 + pr_info("--- chacha20 s390 test end ---\n"); 343 + 344 + out: 345 + if (plain) 346 + vfree(plain); 347 + if (cipher_generic) 348 + vfree(cipher_generic); 349 + if (cipher_s390) 350 + vfree(cipher_s390); 351 + if (revert) 352 + vfree(revert); 353 + 354 + return -1; 355 + } 356 + 357 + static void __exit chacha_s390_test_exit(void) 358 + { 359 + pr_info("s390 ChaCha20 test module exit\n"); 360 + } 361 + 362 + module_param_named(size, data_size, uint, 0660); 363 + module_param(debug, int, 0660); 364 + MODULE_PARM_DESC(size, "Size of a plaintext"); 365 + MODULE_PARM_DESC(debug, "Debug level (0=off,1=on)"); 366 + 367 + module_init(chacha_s390_test_init); 368 + module_exit(chacha_s390_test_exit); 369 + 370 + MODULE_DESCRIPTION("s390 ChaCha20 self-test"); 371 + MODULE_AUTHOR("Vladis Dronov <vdronoff@gmail.com>"); 372 + MODULE_LICENSE("GPL v2");