public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/2] Add Nitro Secure Module support
@ 2023-09-29 21:37 Alexander Graf
  2023-09-29 21:37 ` [PATCH v3 1/2] misc: Add Nitro Secure Module driver Alexander Graf
  2023-09-29 21:37 ` [PATCH v3 2/2] hwrng: Add support for Nitro Secure Module Alexander Graf
  0 siblings, 2 replies; 3+ messages in thread
From: Alexander Graf @ 2023-09-29 21:37 UTC (permalink / raw)
  To: linux-crypto
  Cc: linux-kernel, Greg Kroah-Hartman, Arnd Bergmann, Herbert Xu,
	Olivia Mackall, Petre Eftime, Erdem Meydanlli,
	Benjamin Herrenschmidt, David Woodhouse, Michael S . Tsirkin,
	Jason Wang, Xuan Zhuo

We already have support for the Nitro Enclave kernel module in upstream
Linux, which is needed to control a Nitro Enclave's lifecycle.

However, users typically want to run Linux inside the Enclave as well. To
do that well, they need the ability to communicate to the Nitro Secure
Module: A virtio based PV device that provides access to PCRs, an attestation
document as well as access to entropy.

These patches add driver support for NSM. With them in place, upstream Linux
has everything that's needed to run as a Nitro Enclave kernel.


Alex

v1 -> v2:

   - Remove boilerplate
   - Add uapi header

v2 -> v3:

   - Move globals to device struct
   - Add compat handling
   - Simplify some naming
   - Remove debug prints
   - Use module_virtio_driver
   - Ensure remove only happens on target device
   - Drop use of uio.h

Alexander Graf (2):
  misc: Add Nitro Secure Module driver
  hwrng: Add support for Nitro Secure Module

 MAINTAINERS                      |  11 +
 drivers/char/hw_random/Kconfig   |  12 +
 drivers/char/hw_random/Makefile  |   1 +
 drivers/char/hw_random/nsm-rng.c | 275 ++++++++++++++++++++
 drivers/misc/Kconfig             |  11 +
 drivers/misc/Makefile            |   1 +
 drivers/misc/nsm.c               | 423 +++++++++++++++++++++++++++++++
 include/linux/nsm.h              |  35 +++
 include/uapi/linux/nsm.h         |  30 +++
 9 files changed, 799 insertions(+)
 create mode 100644 drivers/char/hw_random/nsm-rng.c
 create mode 100644 drivers/misc/nsm.c
 create mode 100644 include/linux/nsm.h
 create mode 100644 include/uapi/linux/nsm.h

-- 
2.40.1




Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879




^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH v3 1/2] misc: Add Nitro Secure Module driver
  2023-09-29 21:37 [PATCH v3 0/2] Add Nitro Secure Module support Alexander Graf
@ 2023-09-29 21:37 ` Alexander Graf
  2023-09-29 21:37 ` [PATCH v3 2/2] hwrng: Add support for Nitro Secure Module Alexander Graf
  1 sibling, 0 replies; 3+ messages in thread
From: Alexander Graf @ 2023-09-29 21:37 UTC (permalink / raw)
  To: linux-crypto
  Cc: linux-kernel, Greg Kroah-Hartman, Arnd Bergmann, Herbert Xu,
	Olivia Mackall, Petre Eftime, Erdem Meydanlli,
	Benjamin Herrenschmidt, David Woodhouse, Michael S . Tsirkin,
	Jason Wang, Xuan Zhuo

When running Linux inside a Nitro Enclave, the hypervisor provides a
special virtio device called "NSM". This device has 2 main functions:

  1) Provide attestation reports
  2) Modify PCR state
  3) Provide entropy

This patch adds the core NSM driver that exposes a /dev/nsm device node
which user space can use to request attestation documents and influence
PCR states. A follow up patch will add a hwrng driver to feed its entropy
into the kernel.

Originally-by: Petre Eftime <petre.eftime@gmail.com>
Signed-off-by: Alexander Graf <graf@amazon.com>

---

v1 -> v2:

  - Remove boilerplate
  - Add uapi header

v2 -> v3:

  - Move globals to device struct
  - Add compat handling
  - Simplify some naming
  - Remove debug prints
  - Use module_virtio_driver
  - Drop use of uio.h
---
 MAINTAINERS              |  10 +
 drivers/misc/Kconfig     |  11 +
 drivers/misc/Makefile    |   1 +
 drivers/misc/nsm.c       | 423 +++++++++++++++++++++++++++++++++++++++
 include/linux/nsm.h      |  35 ++++
 include/uapi/linux/nsm.h |  29 +++
 6 files changed, 509 insertions(+)
 create mode 100644 drivers/misc/nsm.c
 create mode 100644 include/linux/nsm.h
 create mode 100644 include/uapi/linux/nsm.h

diff --git a/MAINTAINERS b/MAINTAINERS
index b19995690904..d54bf3ea2b9d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15098,6 +15098,16 @@ F:	include/linux/nitro_enclaves.h
 F:	include/uapi/linux/nitro_enclaves.h
 F:	samples/nitro_enclaves/
 
+NITRO SECURE MODULE (NSM)
+M:	Alexander Graf <graf@amazon.com>
+L:	linux-kernel@vger.kernel.org
+L:	The AWS Nitro Enclaves Team <aws-nitro-enclaves-devel@amazon.com>
+S:	Supported
+W:	https://aws.amazon.com/ec2/nitro/nitro-enclaves/
+F:	drivers/misc/nsm.c
+F:	include/linux/nsm.h
+F:	include/uapi/linux/nsm.h
+
 NOHZ, DYNTICKS SUPPORT
 M:	Frederic Weisbecker <frederic@kernel.org>
 M:	Thomas Gleixner <tglx@linutronix.de>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index cadd4a820c03..7b1ed309d469 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -562,6 +562,17 @@ config TPS6594_PFSM
 	  This driver can also be built as a module.  If so, the module
 	  will be called tps6594-pfsm.
 
+config NSM
+	tristate "Nitro (Enclaves) Security Module support"
+	depends on VIRTIO
+	help
+	  This driver provides support for the Nitro Security Module
+	  in AWS EC2 Nitro based Enclaves. The driver exposes a /dev/nsm
+	  device user space can use to communicate with the hypervisor.
+
+	  To compile this driver as a module, choose M here.
+	  The module will be called nsm.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index f2a4d1ff65d4..ea6ea5bbbc9c 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -67,3 +67,4 @@ obj-$(CONFIG_TMR_MANAGER)      += xilinx_tmr_manager.o
 obj-$(CONFIG_TMR_INJECT)	+= xilinx_tmr_inject.o
 obj-$(CONFIG_TPS6594_ESM)	+= tps6594-esm.o
 obj-$(CONFIG_TPS6594_PFSM)	+= tps6594-pfsm.o
+obj-$(CONFIG_NSM)		+= nsm.o
diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c
new file mode 100644
index 000000000000..c4fd072babdf
--- /dev/null
+++ b/drivers/misc/nsm.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Amazon Nitro Secure Module driver.
+ *
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ */
+
+#include <linux/nsm.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ids.h>
+#include <linux/wait.h>
+#include <uapi/linux/nsm.h>
+
+#define NSM_REQUEST_MAX_SIZE  0x1000
+#define NSM_RESPONSE_MAX_SIZE 0x3000
+
+/* Timeout for NSM virtqueue respose in milliseconds. */
+#define NSM_DEFAULT_TIMEOUT_MSECS (120000) /* 2 minutes */
+
+struct nsm {
+	struct list_head       node;
+	struct virtio_device   *vdev;
+	struct virtqueue       *vq;
+	struct mutex           lock;
+	wait_queue_head_t      wq;
+	bool                   device_notified;
+	struct miscdevice      misc;
+};
+
+/* NSM device ID */
+static const struct virtio_device_id id_table[] = {
+	{ VIRTIO_ID_NITRO_SEC_MOD, VIRTIO_DEV_ANY_ID },
+	{ 0 },
+};
+
+/*
+ * We want to support nsm_rng, but not link the modules. So we create this
+ * intermediate layer to allow the rng driver to register lazily into us.
+ */
+static struct nsm_hwrng *nsm_hwrng;
+
+/*
+ * The rng driver can probe at any time, even after we already finished
+ * initializing all nsm devices. Keep a list of all devices around so that
+ * we can establish the link dynamically
+ */
+static LIST_HEAD(nsm_devices);
+
+static inline struct nsm *to_nsm(struct file *file)
+{
+	return container_of(file->private_data, struct nsm, misc);
+}
+
+/* Copy an entire message from user-space to kernel-space */
+static int message_memdup_from_user(struct nsm_kernel_message *dst, u64 src_addr)
+{
+	struct nsm_message shallow_copy;
+
+	if (!src_addr || !dst)
+		return -EINVAL;
+
+	/* The destination's request and response buffers should be NULL. */
+	if (dst->request.iov_base || dst->response.iov_base)
+		return -EINVAL;
+
+	/* First, make a shallow copy to be able to read the inner pointers */
+	if (copy_from_user(&shallow_copy, u64_to_user_ptr(src_addr),
+			   sizeof(shallow_copy)) != 0)
+		return -EINVAL;
+
+	/* Verify the user input size. */
+	if (shallow_copy.request.len > NSM_REQUEST_MAX_SIZE)
+		return -EMSGSIZE;
+
+	/* Allocate kernel memory for the user request */
+	dst->request.iov_len = shallow_copy.request.len;
+	dst->request.iov_base = kmalloc(dst->request.iov_len, GFP_KERNEL);
+	if (!dst->request.iov_base)
+		return -ENOMEM;
+
+	/* Copy the request content */
+	if (copy_from_user(dst->request.iov_base,
+			   u64_to_user_ptr(shallow_copy.request.addr),
+			   dst->request.iov_len) != 0) {
+		kfree(dst->request.iov_base);
+		return -EFAULT;
+	}
+
+	/* Allocate kernel memory for the response, up to a fixed limit */
+	dst->response.iov_len = shallow_copy.response.len;
+	if (dst->response.iov_len > NSM_RESPONSE_MAX_SIZE)
+		dst->response.iov_len = NSM_RESPONSE_MAX_SIZE;
+
+	dst->response.iov_base = kmalloc(dst->response.iov_len, GFP_KERNEL);
+	if (!dst->response.iov_base) {
+		kfree(dst->request.iov_base);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/* Copy a message back to user-space */
+static int message_copy_to_user(u64 user_addr, struct nsm_kernel_message *kern_msg)
+{
+	struct nsm_message shallow_copy;
+
+	if (!kern_msg || !user_addr)
+		return -EINVAL;
+
+	/*
+	 * First, do a shallow copy of the user-space message. This is needed in
+	 * order to get the request block data, which we do not need to copy but
+	 * must preserve in the message sent back to user-space.
+	 */
+	if (copy_from_user(&shallow_copy, u64_to_user_ptr(user_addr),
+			   sizeof(shallow_copy)) != 0)
+		return -EINVAL;
+
+	/* Do not exceed the capacity of the user-provided response buffer */
+	shallow_copy.response.len = kern_msg->response.iov_len;
+
+	/* Only the response content must be copied back to user-space */
+	if (copy_to_user(u64_to_user_ptr(shallow_copy.response.addr),
+		kern_msg->response.iov_base,
+		shallow_copy.response.len) != 0)
+		return -EINVAL;
+
+	if (copy_to_user(u64_to_user_ptr(user_addr), &shallow_copy,
+			 sizeof(shallow_copy)) != 0)
+		return -EFAULT;
+
+	return 0;
+}
+
+/* Virtqueue interrupt handler */
+static void nsm_vq_callback(struct virtqueue *vq)
+{
+	struct nsm *nsm = vq->vdev->priv;
+
+	nsm->device_notified = true;
+	wake_up(&nsm->wq);
+}
+
+/* Forward a message to the NSM device and wait for the response from it */
+int nsm_communicate_with_device(struct virtio_device *vdev,
+				struct nsm_kernel_message *message)
+{
+	struct nsm *nsm = vdev->priv;
+	struct virtqueue *vq = nsm->vq;
+	struct scatterlist sg_in, sg_out;
+	unsigned int len;
+	void *queue_buf;
+	bool kicked;
+	int rc;
+
+	if (!vdev)
+		return -EINVAL;
+
+	if (!message)
+		return -EINVAL;
+
+	/* Verify if buffer memory is valid. */
+	if (!virt_addr_valid(message->request.iov_base) ||
+		!virt_addr_valid(((u8 *)message->request.iov_base) +
+			message->request.iov_len - 1) ||
+		!virt_addr_valid(message->response.iov_base) ||
+		!virt_addr_valid(((u8 *)message->response.iov_base) +
+			message->response.iov_len - 1))
+		return -EINVAL;
+
+	/* Initialize scatter-gather lists with request and response buffers. */
+	sg_init_one(&sg_out, message->request.iov_base,
+		message->request.iov_len);
+	sg_init_one(&sg_in, message->response.iov_base,
+		message->response.iov_len);
+
+	mutex_lock(&nsm->lock);
+
+	/* Add the request buffer (read by the device). */
+	rc = virtqueue_add_outbuf(vq, &sg_out, 1, message->request.iov_base,
+		GFP_KERNEL);
+	if (rc) {
+		mutex_unlock(&nsm->lock);
+		return rc;
+	}
+
+	/* Add the response buffer (written by the device). */
+	rc = virtqueue_add_inbuf(vq, &sg_in, 1, message->response.iov_base,
+		GFP_KERNEL);
+	if (rc)
+		goto cleanup;
+
+	nsm->device_notified = false;
+	kicked = virtqueue_kick(vq);
+	if (!kicked) {
+		/* Cannot kick the virtqueue. */
+		rc = -EIO;
+		goto cleanup;
+	}
+
+	/* If the kick succeeded, wait for the device's response. */
+	rc = wait_event_timeout(nsm->wq,
+		nsm->device_notified == true,
+		msecs_to_jiffies(NSM_DEFAULT_TIMEOUT_MSECS));
+	if (!rc) {
+		rc = -ETIMEDOUT;
+		goto cleanup;
+	}
+
+	queue_buf = virtqueue_get_buf(vq, &len);
+	if (!queue_buf || (queue_buf != message->request.iov_base)) {
+		pr_err("NSM device received wrong request buffer.");
+		rc = -ENODATA;
+		goto cleanup;
+	}
+
+	queue_buf = virtqueue_get_buf(vq, &len);
+	if (!queue_buf || (queue_buf != message->response.iov_base)) {
+		pr_err("NSM device received wrong response buffer.");
+		rc = -ENODATA;
+		goto cleanup;
+	}
+
+	/* Make sure the response length doesn't exceed the buffer capacity. */
+	if (len < message->response.iov_len)
+		message->response.iov_len = len;
+
+	rc = 0;
+
+cleanup:
+	if (rc) {
+		/* Clean the virtqueue. */
+		while (virtqueue_get_buf(vq, &len) != NULL)
+			;
+	}
+
+	mutex_unlock(&nsm->lock);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(nsm_communicate_with_device);
+
+static long nsm_dev_ioctl(struct file *file, unsigned int cmd,
+	unsigned long arg)
+{
+	struct nsm_kernel_message message = {};
+	struct nsm *nsm = to_nsm(file);
+	int status = 0;
+
+	if (cmd != NSM_IOCTL_REQUEST)
+		return -EINVAL;
+
+	/* Copy the message from user-space to kernel-space */
+	status = message_memdup_from_user(&message, arg);
+	if (status != 0)
+		return status;
+
+	/* Communicate with the NSM device */
+	status = nsm_communicate_with_device(nsm->vdev, &message);
+
+	if (status != 0)
+		goto out;
+
+	/* Copy the response back to user-space */
+	status = message_copy_to_user(arg, &message);
+
+out:
+	/* At this point, everything succeeded, so clean up and finish. */
+	kfree(message.request.iov_base);
+	kfree(message.response.iov_base);
+
+	return status;
+}
+
+static int nsm_dev_file_open(struct inode *node, struct file *file)
+{
+	return 0;
+}
+
+static int nsm_dev_file_close(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static int nsm_device_init_vq(struct virtio_device *vdev)
+{
+	struct virtqueue *vq = virtio_find_single_vq(vdev,
+		nsm_vq_callback, "nsm.vq.0");
+	struct nsm *nsm = vdev->priv;
+
+	if (IS_ERR(vq))
+		return PTR_ERR(vq);
+
+	nsm->vq = vq;
+
+	return 0;
+}
+
+static const struct file_operations nsm_dev_fops = {
+	.open = nsm_dev_file_open,
+	.release = nsm_dev_file_close,
+	.unlocked_ioctl = nsm_dev_ioctl,
+	.compat_ioctl = compat_ptr_ioctl,
+};
+
+/* Handler for probing the NSM device */
+static int nsm_device_probe(struct virtio_device *vdev)
+{
+	struct nsm *nsm;
+	int rc;
+
+	nsm = kzalloc(sizeof(*nsm), GFP_KERNEL);
+	if (!nsm)
+		return -ENOMEM;
+
+	vdev->priv = nsm;
+	nsm->vdev = vdev;
+
+	rc = nsm_device_init_vq(vdev);
+	if (rc) {
+		pr_err("NSM device queue failed to initialize: %d.\n", rc);
+		goto err_init_vq;
+	}
+
+	mutex_init(&nsm->lock);
+	init_waitqueue_head(&nsm->wq);
+
+	nsm->misc = (struct miscdevice) {
+		.minor	= MISC_DYNAMIC_MINOR,
+		.name	= "nsm",
+		.fops	= &nsm_dev_fops,
+		.mode	= 0666
+	};
+
+	rc = misc_register(&nsm->misc);
+	if (rc) {
+		pr_err("NSM misc device registration error: %d.\n", rc);
+		goto err_misc;
+	}
+
+	if (nsm_hwrng)
+		nsm_hwrng->probe(vdev);
+
+	list_add(&nsm->node, &nsm_devices);
+
+	return 0;
+
+err_misc:
+	vdev->config->del_vqs(vdev);
+err_init_vq:
+	kfree(nsm);
+	return rc;
+}
+
+/* Handler for removing the NSM device */
+static void nsm_device_remove(struct virtio_device *vdev)
+{
+	struct nsm *nsm = vdev->priv;
+
+	if (nsm_hwrng)
+		nsm_hwrng->remove(vdev);
+
+	vdev->config->del_vqs(vdev);
+	misc_deregister(&nsm->misc);
+	list_del(&nsm->node);
+}
+
+int nsm_register_hwrng(struct nsm_hwrng *_nsm_hwrng)
+{
+	struct nsm *nsm;
+
+	if (nsm_hwrng)
+		return -EEXIST;
+
+	nsm_hwrng = _nsm_hwrng;
+
+	list_for_each_entry(nsm, &nsm_devices, node)
+		nsm_hwrng->probe(nsm->vdev);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(nsm_register_hwrng);
+
+void nsm_unregister_hwrng(struct nsm_hwrng *_nsm_hwrng)
+{
+	struct nsm *nsm;
+
+	if (_nsm_hwrng != nsm_hwrng)
+		return;
+
+	list_for_each_entry(nsm, &nsm_devices, node)
+		nsm_hwrng->remove(nsm->vdev);
+	nsm_hwrng = NULL;
+}
+EXPORT_SYMBOL_GPL(nsm_unregister_hwrng);
+
+/* NSM device configuration structure */
+static struct virtio_driver virtio_nsm_driver = {
+	.feature_table             = 0,
+	.feature_table_size        = 0,
+	.feature_table_legacy      = 0,
+	.feature_table_size_legacy = 0,
+	.driver.name               = KBUILD_MODNAME,
+	.driver.owner              = THIS_MODULE,
+	.id_table                  = id_table,
+	.probe                     = nsm_device_probe,
+	.remove                    = nsm_device_remove,
+};
+
+module_virtio_driver(virtio_nsm_driver);
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio NSM driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/nsm.h b/include/linux/nsm.h
new file mode 100644
index 000000000000..82753de5485e
--- /dev/null
+++ b/include/linux/nsm.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Amazon Nitro Secure Module driver.
+ *
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ */
+
+#ifndef __LINUX_NSM_H
+#define __LINUX_NSM_H
+
+#include <linux/uio.h>
+#include <linux/virtio.h>
+
+#define NSM_RESPONSE_MAX_SIZE 0x3000
+
+struct nsm_hwrng {
+	int (*probe)(struct virtio_device *dev);
+	void (*remove)(struct virtio_device *dev);
+};
+
+int nsm_register_hwrng(struct nsm_hwrng *nsm_hwrng);
+void nsm_unregister_hwrng(struct nsm_hwrng *nsm_hwrng);
+
+/* Copy of NSM message in kernel-space */
+struct nsm_kernel_message {
+	/* Copy of user request in kernel memory */
+	struct kvec request;
+	/* Copy of user response in kernel memory */
+	struct kvec response;
+};
+
+int nsm_communicate_with_device(struct virtio_device *dev,
+				struct nsm_kernel_message *message);
+
+#endif /* __LINUX_NSM_H */
diff --git a/include/uapi/linux/nsm.h b/include/uapi/linux/nsm.h
new file mode 100644
index 000000000000..4100f8c69f36
--- /dev/null
+++ b/include/uapi/linux/nsm.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ */
+
+#ifndef __UAPI_LINUX_NSM_H
+#define __UAPI_LINUX_NSM_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+struct nsm_iovec
+{
+	__u64 addr; /* Virtual address of target buffer */
+	__u64 len;  /* Length of target buffer */
+};
+
+/* NSM message from user-space */
+struct nsm_message {
+	/* Request from user */
+	struct nsm_iovec request;
+	/* Response to user */
+	struct nsm_iovec response;
+};
+
+#define NSM_MAGIC		0x0A
+#define NSM_IOCTL_REQUEST	_IOWR(NSM_MAGIC, 0, struct nsm_message)
+
+#endif /* __UAPI_LINUX_MISC_BCM_VK_H */
-- 
2.40.1




Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879




^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH v3 2/2] hwrng: Add support for Nitro Secure Module
  2023-09-29 21:37 [PATCH v3 0/2] Add Nitro Secure Module support Alexander Graf
  2023-09-29 21:37 ` [PATCH v3 1/2] misc: Add Nitro Secure Module driver Alexander Graf
@ 2023-09-29 21:37 ` Alexander Graf
  1 sibling, 0 replies; 3+ messages in thread
From: Alexander Graf @ 2023-09-29 21:37 UTC (permalink / raw)
  To: linux-crypto
  Cc: linux-kernel, Greg Kroah-Hartman, Arnd Bergmann, Herbert Xu,
	Olivia Mackall, Petre Eftime, Erdem Meydanlli,
	Benjamin Herrenschmidt, David Woodhouse, Michael S . Tsirkin,
	Jason Wang, Xuan Zhuo

When running Linux inside a Nitro Enclave, the Nitro Secure Module
provides a virtio message that can be used to receive entropy. This
patch adds support to read that entropy on demand and expose it through
the hwrng device.

Originally-by: Petre Eftime <petre.eftime@gmail.com>
Signed-off-by: Alexander Graf <graf@amazon.com>

---

v1 -> v2:

  - Remove boilerplate

v2 -> v3:

  - Ensure remove only happens on target device
---
 MAINTAINERS                      |   1 +
 drivers/char/hw_random/Kconfig   |  12 ++
 drivers/char/hw_random/Makefile  |   1 +
 drivers/char/hw_random/nsm-rng.c | 275 +++++++++++++++++++++++++++++++
 4 files changed, 289 insertions(+)
 create mode 100644 drivers/char/hw_random/nsm-rng.c

diff --git a/MAINTAINERS b/MAINTAINERS
index d54bf3ea2b9d..da9697639968 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15104,6 +15104,7 @@ L:	linux-kernel@vger.kernel.org
 L:	The AWS Nitro Enclaves Team <aws-nitro-enclaves-devel@amazon.com>
 S:	Supported
 W:	https://aws.amazon.com/ec2/nitro/nitro-enclaves/
+F:	drivers/char/hw_random/nsm-rng.c
 F:	drivers/misc/nsm.c
 F:	include/linux/nsm.h
 F:	include/uapi/linux/nsm.h
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 8de74dcfa18c..5d06e24cfdde 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -573,6 +573,18 @@ config HW_RANDOM_JH7110
 	  To compile this driver as a module, choose M here.
 	  The module will be called jh7110-trng.
 
+config HW_RANDOM_NSM
+	tristate "Nitro (Enclaves) Security Module support"
+	depends on NSM
+	help
+	  This driver provides support for the Nitro Security Module
+	  in AWS EC2 Nitro based Enclaves. The driver enables support
+	  for reading RNG data as well as a generic communication
+	  mechanism with the hypervisor.
+
+	  To compile this driver as a module, choose M here.
+	  The module will be called nsm_rng.
+
 endif # HW_RANDOM
 
 config UML_RANDOM
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index 32549a1186dc..7e33d1ed40f8 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -49,3 +49,4 @@ obj-$(CONFIG_HW_RANDOM_ARM_SMCCC_TRNG) += arm_smccc_trng.o
 obj-$(CONFIG_HW_RANDOM_CN10K) += cn10k-rng.o
 obj-$(CONFIG_HW_RANDOM_POLARFIRE_SOC) += mpfs-rng.o
 obj-$(CONFIG_HW_RANDOM_JH7110) += jh7110-trng.o
+obj-$(CONFIG_HW_RANDOM_NSM) += nsm-rng.o
diff --git a/drivers/char/hw_random/nsm-rng.c b/drivers/char/hw_random/nsm-rng.c
new file mode 100644
index 000000000000..556535ef87fe
--- /dev/null
+++ b/drivers/char/hw_random/nsm-rng.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Amazon Nitro Secure Module HWRNG driver.
+ *
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ */
+
+#include <linux/nsm.h>
+#include <linux/hw_random.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/virtio_ids.h>
+
+struct nsm_rng_info {
+	struct hwrng hwrng;
+	struct virtio_device *vdev;
+};
+
+#define CBOR_TYPE_MASK  0xE0
+#define CBOR_TYPE_MAP 0xA0
+#define CBOR_TYPE_TEXT 0x60
+#define CBOR_TYPE_ARRAY 0x40
+#define CBOR_HEADER_SIZE_SHORT 1
+
+#define CBOR_SHORT_SIZE_MAX_VALUE 23
+#define CBOR_LONG_SIZE_U8  24
+#define CBOR_LONG_SIZE_U16 25
+#define CBOR_LONG_SIZE_U32 26
+#define CBOR_LONG_SIZE_U64 27
+
+#define CBOR_HEADER_SIZE_U8  (CBOR_HEADER_SIZE_SHORT + sizeof(u8))
+#define CBOR_HEADER_SIZE_U16 (CBOR_HEADER_SIZE_SHORT + sizeof(u16))
+#define CBOR_HEADER_SIZE_U32 (CBOR_HEADER_SIZE_SHORT + sizeof(u32))
+#define CBOR_HEADER_SIZE_U64 (CBOR_HEADER_SIZE_SHORT + sizeof(u64))
+
+static bool cbor_object_is_array(const u8 *cbor_object, size_t cbor_object_size)
+{
+	if (cbor_object_size == 0 || cbor_object == NULL)
+		return false;
+
+	return (cbor_object[0] & CBOR_TYPE_MASK) == CBOR_TYPE_ARRAY;
+}
+
+static int cbor_object_get_array(u8 *cbor_object, size_t cbor_object_size, u8 **cbor_array)
+{
+	u8 cbor_short_size;
+	u64 array_len;
+	u64 array_offset;
+
+	if (!cbor_object_is_array(cbor_object, cbor_object_size))
+		return -EFAULT;
+
+	if (cbor_array == NULL)
+		return -EFAULT;
+
+	cbor_short_size = (cbor_object[0] & 0x1F);
+
+	/* Decoding byte array length */
+	/* In short field encoding, the object header is 1 byte long and
+	 * contains the type on the 3 MSB and the length on the LSB.
+	 * If the length in the LSB is larger than 23, then the object
+	 * uses long field encoding, and will contain the length over the
+	 * next bytes in the object, depending on the value:
+	 * 24 is u8, 25 is u16, 26 is u32 and 27 is u64.
+	 */
+	if (cbor_short_size <= CBOR_SHORT_SIZE_MAX_VALUE) {
+		/* short encoding */
+		array_len = cbor_short_size;
+		array_offset = CBOR_HEADER_SIZE_SHORT;
+	} else if (cbor_short_size == CBOR_LONG_SIZE_U8) {
+		if (cbor_object_size < CBOR_HEADER_SIZE_U8)
+			return -EFAULT;
+		/* 1 byte */
+		array_len = cbor_object[1];
+		array_offset = CBOR_HEADER_SIZE_U8;
+	} else if (cbor_short_size == CBOR_LONG_SIZE_U16) {
+		if (cbor_object_size < CBOR_HEADER_SIZE_U16)
+			return -EFAULT;
+		/* 2 bytes */
+		array_len = cbor_object[1] << 8 | cbor_object[2];
+		array_offset = CBOR_HEADER_SIZE_U16;
+	} else if (cbor_short_size == CBOR_LONG_SIZE_U32) {
+		if (cbor_object_size < CBOR_HEADER_SIZE_U32)
+			return -EFAULT;
+		/* 4 bytes */
+		array_len = cbor_object[1] << 24 |
+			cbor_object[2] << 16 |
+			cbor_object[3] << 8  |
+			cbor_object[4];
+		array_offset = CBOR_HEADER_SIZE_U32;
+	} else if (cbor_short_size == CBOR_LONG_SIZE_U64) {
+		if (cbor_object_size < CBOR_HEADER_SIZE_U64)
+			return -EFAULT;
+		/* 8 bytes */
+		array_len = (u64) cbor_object[1] << 56 |
+			  (u64) cbor_object[2] << 48 |
+			  (u64) cbor_object[3] << 40 |
+			  (u64) cbor_object[4] << 32 |
+			  (u64) cbor_object[5] << 24 |
+			  (u64) cbor_object[6] << 16 |
+			  (u64) cbor_object[7] << 8  |
+			  (u64) cbor_object[8];
+		array_offset = CBOR_HEADER_SIZE_U64;
+	}
+
+	if (cbor_object_size < array_offset)
+		return -EFAULT;
+
+	if (cbor_object_size - array_offset < array_len)
+		return -EFAULT;
+
+	if (array_len > INT_MAX)
+		return -EFAULT;
+
+	*cbor_array = cbor_object + array_offset;
+	return array_len;
+}
+
+static int nsm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+	struct nsm_rng_info *nsm_rng_info = (struct nsm_rng_info *)rng;
+	struct nsm_kernel_message message = {};
+	int rc = 0;
+	u8 *resp_ptr = NULL;
+	u64 resp_len = 0;
+	u8 *rand_data = NULL;
+	/*
+	 * 69                          # text(9)
+	 *     47657452616E646F6D      # "GetRandom"
+	 */
+	const u8 request[] = { CBOR_TYPE_TEXT + strlen("GetRandom"),
+			       'G', 'e', 't', 'R', 'a', 'n', 'd', 'o', 'm' };
+	/*
+	 * A1                          # map(1)
+	 *     69                      # text(9) - Name of field
+	 *         47657452616E646F6D  # "GetRandom"
+	 * A1                          # map(1) - The field itself
+	 *     66                      # text(6)
+	 *         72616E646F6D        # "random"
+	 *	# The rest of the response should be a byte array
+	 */
+	const u8 response[] = { CBOR_TYPE_MAP + 1,
+				CBOR_TYPE_TEXT + strlen("GetRandom"),
+				'G', 'e', 't', 'R', 'a', 'n', 'd', 'o', 'm',
+				CBOR_TYPE_MAP + 1,
+				CBOR_TYPE_TEXT + strlen("random"),
+				'r', 'a', 'n', 'd', 'o', 'm' };
+
+	/* NSM always needs to wait for a response */
+	if (!wait)
+		return 0;
+
+	/* Set request message */
+	message.request.iov_len = sizeof(request);
+	message.request.iov_base = kmalloc(message.request.iov_len, GFP_KERNEL);
+	if (message.request.iov_base == NULL)
+		goto out;
+	memcpy(message.request.iov_base, request, sizeof(request));
+
+	/* Allocate space for response */
+	message.response.iov_len = NSM_RESPONSE_MAX_SIZE;
+	message.response.iov_base = kmalloc(message.response.iov_len, GFP_KERNEL);
+	if (message.response.iov_base == NULL)
+		goto out;
+
+	/* Send/receive message */
+	rc = nsm_communicate_with_device(nsm_rng_info->vdev, &message);
+	if (rc != 0)
+		goto out;
+
+	resp_ptr = (u8 *) message.response.iov_base;
+	resp_len = message.response.iov_len;
+
+	if (resp_len < sizeof(response) + 1) {
+		pr_err("NSM RNG: Received short response from NSM: Possible error message or invalid response");
+		rc = -EFAULT;
+		goto out;
+	}
+
+	if (memcmp(resp_ptr, response, sizeof(response)) != 0) {
+		pr_err("NSM RNG: Invalid response header: Possible error message or invalid response");
+		rc = -EFAULT;
+		goto out;
+	}
+
+	resp_ptr += sizeof(response);
+	resp_len -= sizeof(response);
+
+	if (!cbor_object_is_array(resp_ptr, resp_len)) {
+		/* not a byte array */
+		pr_err("NSM RNG: Invalid response type: Expecting a byte array response");
+		rc = -EFAULT;
+		goto out;
+	}
+
+	rc = cbor_object_get_array(resp_ptr, resp_len, &rand_data);
+	if (rc < 0) {
+		pr_err("NSM RNG: Invalid CBOR encoding\n");
+		goto out;
+	}
+
+	max = max > INT_MAX ? INT_MAX : max;
+	rc = rc > max ? max : rc;
+	memcpy(data, rand_data, rc);
+
+	pr_debug("NSM RNG: returning rand bytes = %d\n", rc);
+out:
+	kfree(message.request.iov_base);
+	kfree(message.response.iov_base);
+	return rc;
+}
+
+static struct nsm_rng_info nsm_rng_info = {
+	.hwrng = {
+		.read = nsm_rng_read,
+		.name = "nsm-hwrng",
+		.quality = 1000,
+	},
+};
+
+static int nsm_rng_probe(struct virtio_device *vdev)
+{
+	int rc;
+
+	if (nsm_rng_info.vdev)
+		return -EEXIST;
+
+	nsm_rng_info.vdev = vdev;
+	rc = devm_hwrng_register(&vdev->dev, &nsm_rng_info.hwrng);
+
+	if (rc) {
+		pr_err("NSM RNG initialization error: %d.\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static void nsm_rng_remove(struct virtio_device *vdev)
+{
+	if (nsm_rng_info.vdev != vdev)
+		return;
+
+	hwrng_unregister(&nsm_rng_info.hwrng);
+	nsm_rng_info.vdev = NULL;
+}
+
+struct nsm_hwrng nsm_hwrng = {
+	.probe = nsm_rng_probe,
+	.remove = nsm_rng_remove,
+};
+
+static int __init nsm_rng_init(void)
+{
+	return nsm_register_hwrng(&nsm_hwrng);
+}
+
+static void __exit nsm_rng_exit(void)
+{
+	nsm_unregister_hwrng(&nsm_hwrng);
+}
+
+module_init(nsm_rng_init);
+module_exit(nsm_rng_exit);
+
+#ifdef MODULE
+static const struct virtio_device_id nsm_id_table[] = {
+	{ VIRTIO_ID_NITRO_SEC_MOD, VIRTIO_DEV_ANY_ID },
+	{ 0 },
+};
+#endif
+
+MODULE_DEVICE_TABLE(virtio, nsm_id_table);
+MODULE_DESCRIPTION("Virtio NSM RNG driver");
+MODULE_LICENSE("GPL");
-- 
2.40.1




Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879




^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2023-09-29 21:38 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-09-29 21:37 [PATCH v3 0/2] Add Nitro Secure Module support Alexander Graf
2023-09-29 21:37 ` [PATCH v3 1/2] misc: Add Nitro Secure Module driver Alexander Graf
2023-09-29 21:37 ` [PATCH v3 2/2] hwrng: Add support for Nitro Secure Module Alexander Graf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox