mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
369 lines
8.4 KiB
369 lines
8.4 KiB
/* SPDX-License-Identifier: GPL-2.0 |
|
* |
|
* Copyright (C) 2022 Red Hat, Inc. |
|
* Author: Vladis Dronov <[email protected]> |
|
*/ |
|
|
|
#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 = vzalloc(data_size); |
|
if (!cipher_generic) { |
|
pr_info("could not allocate cipher_generic buffer\n"); |
|
ret = -2; |
|
goto out; |
|
} |
|
|
|
cipher_s390 = vzalloc(data_size); |
|
if (!cipher_s390) { |
|
pr_info("could not allocate cipher_s390 buffer\n"); |
|
ret = -2; |
|
goto out; |
|
} |
|
|
|
revert = vzalloc(data_size); |
|
if (!revert) { |
|
pr_info("could not allocate revert buffer\n"); |
|
ret = -2; |
|
goto out; |
|
} |
|
|
|
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 <[email protected]>"); |
|
MODULE_LICENSE("GPL v2");
|
|
|