]> git.baikalelectronics.ru Git - kernel.git/commitdiff
crypto: s390 - add crypto library interface for ChaCha20
authorVladis Dronov <vdronov@redhat.com>
Sun, 8 May 2022 13:09:44 +0000 (15:09 +0200)
committerHerbert Xu <herbert@gondor.apana.org.au>
Fri, 13 May 2022 09:24:49 +0000 (17:24 +0800)
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>
arch/s390/crypto/chacha-glue.c
drivers/crypto/Kconfig
tools/testing/crypto/chacha20-s390/Makefile [new file with mode: 0644]
tools/testing/crypto/chacha20-s390/run-tests.sh [new file with mode: 0644]
tools/testing/crypto/chacha20-s390/test-cipher.c [new file with mode: 0644]

index ccfff73e2c93674134fc07440652c22cb278f6bd..2ec51f339cec44462550d3629de7f79ac95cc260 100644 (file)
@@ -62,6 +62,34 @@ static int chacha20_s390(struct skcipher_request *req)
        return rc;
 }
 
+void hchacha_block_arch(const u32 *state, u32 *stream, int nrounds)
+{
+       /* TODO: implement hchacha_block_arch() in assembly */
+       hchacha_block_generic(state, stream, nrounds);
+}
+EXPORT_SYMBOL(hchacha_block_arch);
+
+void chacha_init_arch(u32 *state, const u32 *key, const u8 *iv)
+{
+       chacha_init_generic(state, key, iv);
+}
+EXPORT_SYMBOL(chacha_init_arch);
+
+void chacha_crypt_arch(u32 *state, u8 *dst, const u8 *src,
+                      unsigned int bytes, int nrounds)
+{
+       /* s390 chacha20 implementation has 20 rounds hard-coded,
+        * it cannot handle a block of data or less, but otherwise
+        * it can handle data of arbitrary size
+        */
+       if (bytes <= CHACHA_BLOCK_SIZE || nrounds != 20)
+               chacha_crypt_generic(state, dst, src, bytes, nrounds);
+       else
+               chacha20_crypt_s390(state, dst, src, bytes,
+                                   &state[4], &state[12]);
+}
+EXPORT_SYMBOL(chacha_crypt_arch);
+
 static struct skcipher_alg chacha_algs[] = {
        {
                .base.cra_name          = "chacha20",
@@ -83,12 +111,14 @@ static struct skcipher_alg chacha_algs[] = {
 
 static int __init chacha_mod_init(void)
 {
-       return crypto_register_skciphers(chacha_algs, ARRAY_SIZE(chacha_algs));
+       return IS_REACHABLE(CONFIG_CRYPTO_SKCIPHER) ?
+               crypto_register_skciphers(chacha_algs, ARRAY_SIZE(chacha_algs)) : 0;
 }
 
 static void __exit chacha_mod_fini(void)
 {
-       crypto_unregister_skciphers(chacha_algs, ARRAY_SIZE(chacha_algs));
+       if (IS_REACHABLE(CONFIG_CRYPTO_SKCIPHER))
+               crypto_unregister_skciphers(chacha_algs, ARRAY_SIZE(chacha_algs));
 }
 
 module_cpu_feature_match(VXRS, chacha_mod_init);
index 7b2d138bc83e05f9b1a4f38c18c4ed57b05f354a..ee99c02c84e81e516b5e986519790ac044472650 100644 (file)
@@ -216,9 +216,9 @@ config CRYPTO_AES_S390
 config CRYPTO_CHACHA_S390
        tristate "ChaCha20 stream cipher"
        depends on S390
-       select CRYPTO_ALGAPI
        select CRYPTO_SKCIPHER
-       select CRYPTO_CHACHA20
+       select CRYPTO_LIB_CHACHA_GENERIC
+       select CRYPTO_ARCH_HAVE_LIB_CHACHA
        help
          This is the s390 SIMD implementation of the ChaCha20 stream
          cipher (RFC 7539).
diff --git a/tools/testing/crypto/chacha20-s390/Makefile b/tools/testing/crypto/chacha20-s390/Makefile
new file mode 100644 (file)
index 0000000..db81cd2
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2022 Red Hat, Inc.
+# Author: Vladis Dronov <vdronoff@gmail.com>
+
+obj-m += test_cipher.o
+test_cipher-y := test-cipher.o
+
+all:
+       make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
+clean:
+       make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
diff --git a/tools/testing/crypto/chacha20-s390/run-tests.sh b/tools/testing/crypto/chacha20-s390/run-tests.sh
new file mode 100644 (file)
index 0000000..4310879
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2022 Red Hat, Inc.
+# Author: Vladis Dronov <vdronoff@gmail.com>
+#
+# This script runs (via instmod) test-cipher.ko module which invokes
+# generic and s390-native ChaCha20 encryprion algorithms with different
+# size of data. Check 'dmesg' for results.
+#
+# The insmod error is expected:
+# insmod: ERROR: could not insert module test_cipher.ko: Operation not permitted
+
+lsmod | grep chacha | cut -f1 -d' ' | xargs rmmod
+modprobe chacha_generic
+modprobe chacha_s390
+
+# run encryption for different data size, including whole block(s) +/- 1
+insmod test_cipher.ko size=63
+insmod test_cipher.ko size=64
+insmod test_cipher.ko size=65
+insmod test_cipher.ko size=127
+insmod test_cipher.ko size=128
+insmod test_cipher.ko size=129
+insmod test_cipher.ko size=511
+insmod test_cipher.ko size=512
+insmod test_cipher.ko size=513
+insmod test_cipher.ko size=4096
+insmod test_cipher.ko size=65611
+insmod test_cipher.ko size=6291456
+insmod test_cipher.ko size=62914560
+
+# print test logs
+dmesg | tail -170
diff --git a/tools/testing/crypto/chacha20-s390/test-cipher.c b/tools/testing/crypto/chacha20-s390/test-cipher.c
new file mode 100644 (file)
index 0000000..34e8b85
--- /dev/null
@@ -0,0 +1,372 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ * Author: Vladis Dronov <vdronoff@gmail.com>
+ */
+
+#include <asm/elf.h>
+#include <asm/uaccess.h>
+#include <asm/smp.h>
+#include <crypto/skcipher.h>
+#include <crypto/akcipher.h>
+#include <crypto/acompress.h>
+#include <crypto/rng.h>
+#include <crypto/drbg.h>
+#include <crypto/kpp.h>
+#include <crypto/internal/simd.h>
+#include <crypto/chacha.h>
+#include <crypto/aead.h>
+#include <crypto/hash.h>
+#include <linux/crypto.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/fips.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/scatterlist.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+#include <linux/zlib.h>
+#include <linux/once.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+static unsigned int data_size __read_mostly = 256;
+static unsigned int debug __read_mostly = 0;
+
+/* tie all skcipher structures together */
+struct skcipher_def {
+       struct scatterlist sginp, sgout;
+       struct crypto_skcipher *tfm;
+       struct skcipher_request *req;
+       struct crypto_wait wait;
+};
+
+/* Perform cipher operations with the chacha lib */
+static int test_lib_chacha(u8 *revert, u8 *cipher, u8 *plain)
+{
+       u32 chacha_state[CHACHA_STATE_WORDS];
+       u8 iv[16], key[32];
+       u64 start, end;
+
+       memset(key, 'X', sizeof(key));
+       memset(iv, 'I', sizeof(iv));
+
+       if (debug) {
+               print_hex_dump(KERN_INFO, "key: ", DUMP_PREFIX_OFFSET,
+                              16, 1, key, 32, 1);
+
+               print_hex_dump(KERN_INFO, "iv:  ", DUMP_PREFIX_OFFSET,
+                              16, 1, iv, 16, 1);
+       }
+
+       /* Encrypt */
+       chacha_init_arch(chacha_state, (u32*)key, iv);
+
+       start = ktime_get_ns();
+       chacha_crypt_arch(chacha_state, cipher, plain, data_size, 20);
+       end = ktime_get_ns();
+
+
+       if (debug)
+               print_hex_dump(KERN_INFO, "encr:", DUMP_PREFIX_OFFSET,
+                              16, 1, cipher,
+                              (data_size > 64 ? 64 : data_size), 1);
+
+       pr_info("lib encryption took: %lld nsec", end - start);
+
+       /* Decrypt */
+       chacha_init_arch(chacha_state, (u32 *)key, iv);
+
+       start = ktime_get_ns();
+       chacha_crypt_arch(chacha_state, revert, cipher, data_size, 20);
+       end = ktime_get_ns();
+
+       if (debug)
+               print_hex_dump(KERN_INFO, "decr:", DUMP_PREFIX_OFFSET,
+                              16, 1, revert,
+                              (data_size > 64 ? 64 : data_size), 1);
+
+       pr_info("lib decryption took: %lld nsec", end - start);
+
+       return 0;
+}
+
+/* Perform cipher operations with skcipher */
+static unsigned int test_skcipher_encdec(struct skcipher_def *sk,
+                                        int enc)
+{
+       int rc;
+
+       if (enc) {
+               rc = crypto_wait_req(crypto_skcipher_encrypt(sk->req),
+                                    &sk->wait);
+               if (rc)
+                       pr_info("skcipher encrypt returned with result"
+                               "%d\n", rc);
+       }
+       else
+       {
+               rc = crypto_wait_req(crypto_skcipher_decrypt(sk->req),
+                                    &sk->wait);
+               if (rc)
+                       pr_info("skcipher decrypt returned with result"
+                               "%d\n", rc);
+       }
+
+       return rc;
+}
+
+/* Initialize and trigger cipher operations */
+static int test_skcipher(char *name, u8 *revert, u8 *cipher, u8 *plain)
+{
+       struct skcipher_def sk;
+       struct crypto_skcipher *skcipher = NULL;
+       struct skcipher_request *req = NULL;
+       u8 iv[16], key[32];
+       u64 start, end;
+       int ret = -EFAULT;
+
+       skcipher = crypto_alloc_skcipher(name, 0, 0);
+       if (IS_ERR(skcipher)) {
+               pr_info("could not allocate skcipher %s handle\n", name);
+               return PTR_ERR(skcipher);
+       }
+
+       req = skcipher_request_alloc(skcipher, GFP_KERNEL);
+       if (!req) {
+               pr_info("could not allocate skcipher request\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+                                         crypto_req_done,
+                                         &sk.wait);
+
+       memset(key, 'X', sizeof(key));
+       memset(iv, 'I', sizeof(iv));
+
+       if (crypto_skcipher_setkey(skcipher, key, 32)) {
+               pr_info("key could not be set\n");
+               ret = -EAGAIN;
+               goto out;
+       }
+
+       if (debug) {
+               print_hex_dump(KERN_INFO, "key: ", DUMP_PREFIX_OFFSET,
+                              16, 1, key, 32, 1);
+
+               print_hex_dump(KERN_INFO, "iv:  ", DUMP_PREFIX_OFFSET,
+                              16, 1, iv, 16, 1);
+       }
+
+       sk.tfm = skcipher;
+       sk.req = req;
+
+       /* Encrypt in one pass */
+       sg_init_one(&sk.sginp, plain, data_size);
+       sg_init_one(&sk.sgout, cipher, data_size);
+       skcipher_request_set_crypt(req, &sk.sginp, &sk.sgout,
+                                  data_size, iv);
+       crypto_init_wait(&sk.wait);
+
+       /* Encrypt data */
+       start = ktime_get_ns();
+       ret = test_skcipher_encdec(&sk, 1);
+       end = ktime_get_ns();
+
+       if (ret)
+               goto out;
+
+       pr_info("%s tfm encryption successful, took %lld nsec\n", name, end - start);
+
+       if (debug)
+               print_hex_dump(KERN_INFO, "encr:", DUMP_PREFIX_OFFSET,
+                              16, 1, cipher,
+                              (data_size > 64 ? 64 : data_size), 1);
+
+       /* Prepare for decryption */
+       memset(iv, 'I', sizeof(iv));
+
+       sg_init_one(&sk.sginp, cipher, data_size);
+       sg_init_one(&sk.sgout, revert, data_size);
+       skcipher_request_set_crypt(req, &sk.sginp, &sk.sgout,
+                                  data_size, iv);
+       crypto_init_wait(&sk.wait);
+
+       /* Decrypt data */
+       start = ktime_get_ns();
+       ret = test_skcipher_encdec(&sk, 0);
+       end = ktime_get_ns();
+
+       if (ret)
+               goto out;
+
+       pr_info("%s tfm decryption successful, took %lld nsec\n", name, end - start);
+
+       if (debug)
+               print_hex_dump(KERN_INFO, "decr:", DUMP_PREFIX_OFFSET,
+                              16, 1, revert,
+                              (data_size > 64 ? 64 : data_size), 1);
+
+       /* Dump some internal skcipher data */
+       if (debug)
+               pr_info("skcipher %s: cryptlen %d blksize %d stride %d "
+                       "ivsize %d alignmask 0x%x\n",
+                       name, sk.req->cryptlen,
+                       crypto_skcipher_blocksize(sk.tfm),
+                       crypto_skcipher_alg(sk.tfm)->walksize,
+                       crypto_skcipher_ivsize(sk.tfm),
+                       crypto_skcipher_alignmask(sk.tfm));
+
+out:
+       if (skcipher)
+               crypto_free_skcipher(skcipher);
+       if (req)
+               skcipher_request_free(req);
+       return ret;
+}
+
+static int __init chacha_s390_test_init(void)
+{
+       u8 *plain = NULL, *revert = NULL;
+       u8 *cipher_generic = NULL, *cipher_s390 = NULL;
+       int ret = -1;
+
+       pr_info("s390 ChaCha20 test module: size=%d debug=%d\n",
+               data_size, debug);
+
+       /* Allocate and fill buffers */
+       plain = vmalloc(data_size);
+       if (!plain) {
+               pr_info("could not allocate plain buffer\n");
+               ret = -2;
+               goto out;
+       }
+       memset(plain, 'a', data_size);
+       get_random_bytes(plain, (data_size > 256 ? 256 : data_size));
+
+       cipher_generic = vmalloc(data_size);
+       if (!cipher_generic) {
+               pr_info("could not allocate cipher_generic buffer\n");
+               ret = -2;
+               goto out;
+       }
+       memset(cipher_generic, 0, data_size);
+
+       cipher_s390 = vmalloc(data_size);
+       if (!cipher_s390) {
+               pr_info("could not allocate cipher_s390 buffer\n");
+               ret = -2;
+               goto out;
+       }
+       memset(cipher_s390, 0, data_size);
+
+       revert = vmalloc(data_size);
+       if (!revert) {
+               pr_info("could not allocate revert buffer\n");
+               ret = -2;
+               goto out;
+       }
+       memset(revert, 0, data_size);
+
+       if (debug)
+               print_hex_dump(KERN_INFO, "src: ", DUMP_PREFIX_OFFSET,
+                              16, 1, plain,
+                              (data_size > 64 ? 64 : data_size), 1);
+
+       /* Use chacha20 generic */
+       ret = test_skcipher("chacha20-generic", revert, cipher_generic, plain);
+       if (ret)
+               goto out;
+
+       if (memcmp(plain, revert, data_size)) {
+               pr_info("generic en/decryption check FAILED\n");
+               ret = -2;
+               goto out;
+       }
+       else
+               pr_info("generic en/decryption check OK\n");
+
+       memset(revert, 0, data_size);
+
+       /* Use chacha20 s390 */
+       ret = test_skcipher("chacha20-s390", revert, cipher_s390, plain);
+       if (ret)
+               goto out;
+
+       if (memcmp(plain, revert, data_size)) {
+               pr_info("s390 en/decryption check FAILED\n");
+               ret = -2;
+               goto out;
+       }
+       else
+               pr_info("s390 en/decryption check OK\n");
+
+       if (memcmp(cipher_generic, cipher_s390, data_size)) {
+               pr_info("s390 vs generic check FAILED\n");
+               ret = -2;
+               goto out;
+       }
+       else
+               pr_info("s390 vs generic check OK\n");
+
+       memset(cipher_s390, 0, data_size);
+       memset(revert, 0, data_size);
+
+       /* Use chacha20 lib */
+       test_lib_chacha(revert, cipher_s390, plain);
+
+       if (memcmp(plain, revert, data_size)) {
+               pr_info("lib en/decryption check FAILED\n");
+               ret = -2;
+               goto out;
+       }
+       else
+               pr_info("lib en/decryption check OK\n");
+
+       if (memcmp(cipher_generic, cipher_s390, data_size)) {
+               pr_info("lib vs generic check FAILED\n");
+               ret = -2;
+               goto out;
+       }
+       else
+               pr_info("lib vs generic check OK\n");
+
+       pr_info("--- chacha20 s390 test end ---\n");
+
+out:
+       if (plain)
+               vfree(plain);
+       if (cipher_generic)
+               vfree(cipher_generic);
+       if (cipher_s390)
+               vfree(cipher_s390);
+       if (revert)
+               vfree(revert);
+
+       return -1;
+}
+
+static void __exit chacha_s390_test_exit(void)
+{
+       pr_info("s390 ChaCha20 test module exit\n");
+}
+
+module_param_named(size, data_size, uint, 0660);
+module_param(debug, int, 0660);
+MODULE_PARM_DESC(size, "Size of a plaintext");
+MODULE_PARM_DESC(debug, "Debug level (0=off,1=on)");
+
+module_init(chacha_s390_test_init);
+module_exit(chacha_s390_test_exit);
+
+MODULE_DESCRIPTION("s390 ChaCha20 self-test");
+MODULE_AUTHOR("Vladis Dronov <vdronoff@gmail.com>");
+MODULE_LICENSE("GPL v2");