From 6db232ea9ebcca23650d15e3606a8f4d48c09ca1 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 15 May 2022 16:01:44 +0200
Subject: [PATCH 05/25] LRNG - add common generic hash support

The LRNG switchable DRNG support also allows the replacement of the hash
implementation used as conditioning component. The common generic hash
support code provides the required callbacks using the synchronous hash
implementations of the kernel crypto API.

All synchronous hash implementations supported by the kernel crypto API
can be used as part of the LRNG with this generic support.

Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
 drivers/char/lrng/Kconfig           |  40 ++++----
 drivers/char/lrng/Makefile          |   1 +
 drivers/char/lrng/lrng_hash_kcapi.c | 140 ++++++++++++++++++++++++++++
 3 files changed, 161 insertions(+), 20 deletions(-)
 create mode 100644 drivers/char/lrng/lrng_hash_kcapi.c

--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -633,26 +633,26 @@ config LRNG_DRNG_CHACHA20
 config LRNG_SWITCH
 	bool
 
-# menuconfig LRNG_SWITCH_HASH
-# 	bool "Support conditioning hash runtime switching"
-# 	select LRNG_SWITCH
-# 	help
-# 	  The LRNG uses a default message digest. With this
-# 	  configuration option other message digests can be selected
-# 	  and loaded at runtime.
-#
-# if LRNG_SWITCH_HASH
-#
-# config LRNG_HASH_KCAPI
-# 	tristate "Kernel crypto API hashing support for LRNG"
-# 	select CRYPTO_HASH
-# 	select CRYPTO_SHA512
-# 	help
-# 	  Enable the kernel crypto API support for entropy compression
-# 	  and conditioning functions.
-#
-# endif # LRNG_SWITCH_HASH
-#
+menuconfig LRNG_SWITCH_HASH
+	bool "Support conditioning hash runtime switching"
+	select LRNG_SWITCH
+	help
+	  The LRNG uses a default message digest. With this
+	  configuration option other message digests can be selected
+	  and loaded at runtime.
+
+if LRNG_SWITCH_HASH
+
+config LRNG_HASH_KCAPI
+	tristate "Kernel crypto API hashing support for LRNG"
+	select CRYPTO_HASH
+	select CRYPTO_SHA512
+	help
+	  Enable the kernel crypto API support for entropy compression
+	  and conditioning functions.
+
+endif # LRNG_SWITCH_HASH
+
 # menuconfig LRNG_SWITCH_DRNG
 # 	bool "Support DRNG runtime switching"
 # 	select LRNG_SWITCH
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -12,4 +12,5 @@ obj-$(CONFIG_SYSCTL)			+= lrng_proc.o
 obj-$(CONFIG_NUMA)			+= lrng_numa.o
 
 obj-$(CONFIG_LRNG_SWITCH)		+= lrng_switch.o
+obj-$(CONFIG_LRNG_HASH_KCAPI)		+= lrng_hash_kcapi.o
 obj-$(CONFIG_LRNG_DRNG_CHACHA20)	+= lrng_drng_chacha20.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_hash_kcapi.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Backend for providing the hash primitive using the kernel crypto API.
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+#include <crypto/hash.h>
+#include <linux/module.h>
+
+
+static char *lrng_hash_name = "sha512";
+
+/* The parameter must be r/o in sysfs as otherwise races appear. */
+module_param(lrng_hash_name, charp, 0444);
+MODULE_PARM_DESC(lrng_hash_name, "Kernel crypto API hash name");
+
+struct lrng_hash_info {
+	struct crypto_shash *tfm;
+};
+
+static const char *lrng_kcapi_hash_name(void)
+{
+	return lrng_hash_name;
+}
+
+static void _lrng_kcapi_hash_free(struct lrng_hash_info *lrng_hash)
+{
+	struct crypto_shash *tfm = lrng_hash->tfm;
+
+	crypto_free_shash(tfm);
+	kfree(lrng_hash);
+}
+
+static void *lrng_kcapi_hash_alloc(const char *name)
+{
+	struct lrng_hash_info *lrng_hash;
+	struct crypto_shash *tfm;
+	int ret;
+
+	if (!name) {
+		pr_err("Hash name missing\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	tfm = crypto_alloc_shash(name, 0, 0);
+	if (IS_ERR(tfm)) {
+		pr_err("could not allocate hash %s\n", name);
+		return ERR_CAST(tfm);
+	}
+
+	ret = sizeof(struct lrng_hash_info);
+	lrng_hash = kmalloc(ret, GFP_KERNEL);
+	if (!lrng_hash) {
+		crypto_free_shash(tfm);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	lrng_hash->tfm = tfm;
+
+	pr_info("Hash %s allocated\n", name);
+
+	return lrng_hash;
+}
+
+static void *lrng_kcapi_hash_name_alloc(void)
+{
+	return lrng_kcapi_hash_alloc(lrng_kcapi_hash_name());
+}
+
+static u32 lrng_kcapi_hash_digestsize(void *hash)
+{
+	struct lrng_hash_info *lrng_hash = (struct lrng_hash_info *)hash;
+	struct crypto_shash *tfm = lrng_hash->tfm;
+
+	return crypto_shash_digestsize(tfm);
+}
+
+static void lrng_kcapi_hash_dealloc(void *hash)
+{
+	struct lrng_hash_info *lrng_hash = (struct lrng_hash_info *)hash;
+
+	_lrng_kcapi_hash_free(lrng_hash);
+	pr_info("Hash deallocated\n");
+}
+
+static int lrng_kcapi_hash_init(struct shash_desc *shash, void *hash)
+{
+	struct lrng_hash_info *lrng_hash = (struct lrng_hash_info *)hash;
+	struct crypto_shash *tfm = lrng_hash->tfm;
+
+	shash->tfm = tfm;
+	return crypto_shash_init(shash);
+}
+
+static int lrng_kcapi_hash_update(struct shash_desc *shash, const u8 *inbuf,
+			   u32 inbuflen)
+{
+	return crypto_shash_update(shash, inbuf, inbuflen);
+}
+
+static int lrng_kcapi_hash_final(struct shash_desc *shash, u8 *digest)
+{
+	return crypto_shash_final(shash, digest);
+}
+
+static void lrng_kcapi_hash_zero(struct shash_desc *shash)
+{
+	shash_desc_zero(shash);
+}
+
+static const struct lrng_hash_cb lrng_kcapi_hash_cb = {
+	.hash_name		= lrng_kcapi_hash_name,
+	.hash_alloc		= lrng_kcapi_hash_name_alloc,
+	.hash_dealloc		= lrng_kcapi_hash_dealloc,
+	.hash_digestsize	= lrng_kcapi_hash_digestsize,
+	.hash_init		= lrng_kcapi_hash_init,
+	.hash_update		= lrng_kcapi_hash_update,
+	.hash_final		= lrng_kcapi_hash_final,
+	.hash_desc_zero		= lrng_kcapi_hash_zero,
+};
+
+static int __init lrng_kcapi_init(void)
+{
+	return lrng_set_hash_cb(&lrng_kcapi_hash_cb);
+}
+
+static void __exit lrng_kcapi_exit(void)
+{
+	lrng_set_hash_cb(NULL);
+}
+
+late_initcall(lrng_kcapi_init);
+module_exit(lrng_kcapi_exit);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
+MODULE_DESCRIPTION("Entropy Source and DRNG Manager - Kernel crypto API hash backend");
