LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [RFC PATCH  0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend
@ 2021-03-03 13:54 Alex Bennée
  2021-03-03 13:54 ` [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem Alex Bennée
                   ` (5 more replies)
  0 siblings, 6 replies; 47+ messages in thread
From: Alex Bennée @ 2021-03-03 13:54 UTC (permalink / raw)
  To: linux-kernel
  Cc: maxim.uvarov, joakim.bech, ilias.apalodimas, arnd, ruchika.gupta,
	tomas.winkler, yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc,
	linux-scsi, linux-nvme, Alex Bennée

Hi,

This is a follow-up to the email I sent last month:

  Subject: RPMB user space ABI
  Date: Thu, 11 Feb 2021 14:07:00 +0000
  Message-ID: <87mtwashi4.fsf@linaro.org>

which attempts to put some concrete flesh on the bones of the proposal
for a new internal kernel API for dealing with RPMB partitions and the
resultant exposed user-space character device API.

It became apparent while implementing a virtio-rpmb backend that the
initial proposed API didn't sit well with a device like virtio-rpmb
which isn't part of a greater device (like eMMC or UFS). It also
exposed the gritty details of the frame format to userspace leaving it
to deal with the complications of creating JDEC frames and calculating
MACs.

The series is based on Thomas' last posting with a bunch of
functionality dropped:

  - no FS/RPMB integration
  - dropped the simulator
  - dropped the sysfs patches

There is a start of a WIP virtio-rpmb front-end however as the initial
discussion should be focused on the proposed APIs I thought it would
be worth posting as an RFC before getting too deep into the weeds of
implementation. The principle changes to the original proposal:

  - frame construction left to device driver

  The differences between UFS/JEDEC/VirtioRPMB are left for the driver
  itself to deal with. This means things like MAC calculation and
  validation also remain the preserve of the low level implementation
  details. This doesn't mean there can't be shared code where
  implementation details are common across several device types.

  - key management uses keyctl()

  This means in theory userspace could interact with the RPMB device
  without having to manage the key itself. This also means you don't
  need to pass as much data about as the kernel internals can just use
  the keyring id with the API to fetch the key when required.

  - user-space interface split across several ioctls

  Now we no longer have multiple command frames going back and forth
  we can have a single structure per ioctl which just contains what is
  needed for the operation in question.

So what do people think? Is it worth pursuing this approach?

I'm certainly intended to complete the virtio-rpmb driver and test it
with my QEMU based vhost-user backend. However I've no direct interest
in implementing the interfaces to real hardware. I leave that to
people who have access to such things and are willing to take up the
maintainer burden if this is merged.


Alex Bennée (5):
  rpmb: add Replay Protected Memory Block (RPMB) subsystem
  char: rpmb: provide a user space interface
  tools rpmb: add RPBM access tool
  rpmb: create virtio rpmb frontend driver [WIP]
  tools/rpmb: simple test sequence

 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 MAINTAINERS                                   |   9 +
 drivers/char/Kconfig                          |   2 +
 drivers/char/Makefile                         |   1 +
 drivers/char/rpmb/Kconfig                     |  28 +
 drivers/char/rpmb/Makefile                    |   9 +
 drivers/char/rpmb/cdev.c                      | 246 +++++++
 drivers/char/rpmb/core.c                      | 431 ++++++++++++
 drivers/char/rpmb/rpmb-cdev.h                 |  17 +
 drivers/char/rpmb/virtio_rpmb.c               | 366 ++++++++++
 include/linux/rpmb.h                          | 173 +++++
 include/uapi/linux/rpmb.h                     |  68 ++
 include/uapi/linux/virtio_ids.h               |   1 +
 include/uapi/linux/virtio_rpmb.h              |  54 ++
 tools/Makefile                                |  14 +-
 tools/rpmb/.gitignore                         |   2 +
 tools/rpmb/Makefile                           |  41 ++
 tools/rpmb/key                                |   1 +
 tools/rpmb/rpmb.c                             | 649 ++++++++++++++++++
 tools/rpmb/test.sh                            |  13 +
 20 files changed, 2121 insertions(+), 5 deletions(-)
 create mode 100644 drivers/char/rpmb/Kconfig
 create mode 100644 drivers/char/rpmb/Makefile
 create mode 100644 drivers/char/rpmb/cdev.c
 create mode 100644 drivers/char/rpmb/core.c
 create mode 100644 drivers/char/rpmb/rpmb-cdev.h
 create mode 100644 drivers/char/rpmb/virtio_rpmb.c
 create mode 100644 include/linux/rpmb.h
 create mode 100644 include/uapi/linux/rpmb.h
 create mode 100644 include/uapi/linux/virtio_rpmb.h
 create mode 100644 tools/rpmb/.gitignore
 create mode 100644 tools/rpmb/Makefile
 create mode 100644 tools/rpmb/key
 create mode 100644 tools/rpmb/rpmb.c
 create mode 100755 tools/rpmb/test.sh

-- 
2.20.1


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

* [RFC PATCH  1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-03 13:54 [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend Alex Bennée
@ 2021-03-03 13:54 ` Alex Bennée
  2021-03-03 15:28   ` Ulf Hansson
  2021-03-04 20:56   ` Arnd Bergmann
  2021-03-03 13:54 ` [RFC PATCH 2/5] char: rpmb: provide a user space interface Alex Bennée
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 47+ messages in thread
From: Alex Bennée @ 2021-03-03 13:54 UTC (permalink / raw)
  To: linux-kernel
  Cc: maxim.uvarov, joakim.bech, ilias.apalodimas, arnd, ruchika.gupta,
	tomas.winkler, yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc,
	linux-scsi, linux-nvme, Alex Bennée, Ulf Hansson,
	Linus Walleij, Arnd Bergmann

A number of storage technologies support a specialised hardware
partition designed to be resistant to replay attacks. The underlying
HW protocols differ but the operations are common. The RPMB partition
cannot be accessed via standard block layer, but by a set of specific
commands: WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY. Such a
partition provides authenticated and replay protected access, hence
suitable as a secure storage.

The RPMB layer aims to provide in-kernel API for Trusted Execution
Environment (TEE) devices that are capable to securely compute block
frame signature. In case a TEE device wishes to store a replay
protected data, requests the storage device via RPMB layer to store
the data.

A TEE device driver can claim the RPMB interface, for example, via
class_interface_register(). The RPMB layer provides a series of
operations for interacting with the device.

  * program_key - a one time operation for setting up a new device
  * get_capacity - introspect the device capacity
  * get_write_count - check the write counter
  * write_blocks - write a series of blocks to the RPMB device
  * read_blocks - read a series of blocks from the RPMB device

The detailed operation of implementing the access is left to the TEE
device driver itself.

[This is based-on Thomas Winkler's proposed API from:

  https://lore.kernel.org/linux-mmc/1478548394-8184-2-git-send-email-tomas.winkler@intel.com/

The principle difference is the framing details and HW specific
bits (JDEC vs NVME frames) are left to the lower level TEE driver to
worry about. The eventual userspace ioctl interface will aim to be
similarly generic. This is an RFC to follow up on:

  Subject: RPMB user space ABI
  Date: Thu, 11 Feb 2021 14:07:00 +0000
  Message-ID: <87mtwashi4.fsf@linaro.org>]

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Cc: Tomas Winkler <tomas.winkler@intel.com>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Linus  Walleij <linus.walleij@linaro.org>
Cc: Arnd Bergmann <arnd.bergmann@linaro.org>
Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
 MAINTAINERS                |   7 +
 drivers/char/Kconfig       |   2 +
 drivers/char/Makefile      |   1 +
 drivers/char/rpmb/Kconfig  |  11 +
 drivers/char/rpmb/Makefile |   7 +
 drivers/char/rpmb/core.c   | 429 +++++++++++++++++++++++++++++++++++++
 include/linux/rpmb.h       | 163 ++++++++++++++
 7 files changed, 620 insertions(+)
 create mode 100644 drivers/char/rpmb/Kconfig
 create mode 100644 drivers/char/rpmb/Makefile
 create mode 100644 drivers/char/rpmb/core.c
 create mode 100644 include/linux/rpmb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index bfc1b86e3e73..076f3983526c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15369,6 +15369,13 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml
 F:	drivers/media/platform/sunxi/sun8i-rotate/
 
+RPMB SUBSYSTEM
+M:	?
+L:	linux-kernel@vger.kernel.org
+S:	Supported
+F:	drivers/char/rpmb/*
+F:	include/linux/rpmb.h
+
 RTL2830 MEDIA DRIVER
 M:	Antti Palosaari <crope@iki.fi>
 L:	linux-media@vger.kernel.org
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index d229a2d0c017..a7834cc3e0ea 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -471,6 +471,8 @@ config ADI
 	  and SSM (Silicon Secured Memory).  Intended consumers of this
 	  driver include crash and makedumpfile.
 
+source "drivers/char/rpmb/Kconfig"
+
 endmenu
 
 config RANDOM_TRUST_CPU
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index ffce287ef415..0eed6e21a7a7 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -47,3 +47,4 @@ obj-$(CONFIG_PS3_FLASH)		+= ps3flash.o
 obj-$(CONFIG_XILLYBUS)		+= xillybus/
 obj-$(CONFIG_POWERNV_OP_PANEL)	+= powernv-op-panel.o
 obj-$(CONFIG_ADI)		+= adi.o
+obj-$(CONFIG_RPMB)		+= rpmb/
diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig
new file mode 100644
index 000000000000..431c2823cf70
--- /dev/null
+++ b/drivers/char/rpmb/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015-2019, Intel Corporation.
+
+config RPMB
+	tristate "RPMB partition interface"
+	help
+	  Unified RPMB partition interface for eMMC and UFS.
+	  Provides interface for in kernel security controllers to
+	  access RPMB partition.
+
+	  If unsure, select N.
diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile
new file mode 100644
index 000000000000..24d4752a9a53
--- /dev/null
+++ b/drivers/char/rpmb/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015-2019, Intel Corporation.
+
+obj-$(CONFIG_RPMB) += rpmb.o
+rpmb-objs += core.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c
new file mode 100644
index 000000000000..a2e21c14986a
--- /dev/null
+++ b/drivers/char/rpmb/core.c
@@ -0,0 +1,429 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(c) 2015 - 2019 Intel Corporation. All rights reserved.
+ * Copyright(c) 2021 - Linaro Ltd.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include <linux/rpmb.h>
+
+static DEFINE_IDA(rpmb_ida);
+
+/**
+ * rpmb_dev_get() - increase rpmb device ref counter
+ * @rdev: rpmb device
+ */
+struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
+{
+	return get_device(&rdev->dev) ? rdev : NULL;
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_get);
+
+/**
+ * rpmb_dev_put() - decrease rpmb device ref counter
+ * @rdev: rpmb device
+ */
+void rpmb_dev_put(struct rpmb_dev *rdev)
+{
+	put_device(&rdev->dev);
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_put);
+
+/**
+ * rpmb_program_key() - program the RPMB access key
+ * @rdev: rpmb device
+ * @key: key data
+ * @keylen: length of key data
+ *
+ * A successful programming of the key implies it has been set by the
+ * driver and can be used.
+ *
+ * Return:
+ * *        0 on success
+ * *        -EINVAL on wrong parameters
+ * *        -EPERM key already programmed
+ * *        -EOPNOTSUPP if device doesn't support the requested operation
+ * *        < 0 if the operation fails
+ */
+int rpmb_program_key(struct rpmb_dev *rdev, key_serial_t keyid)
+{
+	int err;
+
+	if (!rdev || !keyid)
+		return -EINVAL;
+
+	mutex_lock(&rdev->lock);
+	err = -EOPNOTSUPP;
+	if (rdev->ops && rdev->ops->program_key) {
+		err = rdev->ops->program_key(rdev->dev.parent, rdev->target,
+					     keyid);
+	}
+	mutex_unlock(&rdev->lock);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(rpmb_program_key);
+
+/**
+ * rpmb_get_capacity() - returns the capacity of the rpmb device
+ * @rdev: rpmb device
+ *
+ * Return:
+ * *        capacity of the device in units of 128K, on success
+ * *        -EINVAL on wrong parameters
+ * *        -EOPNOTSUPP if device doesn't support the requested operation
+ * *        < 0 if the operation fails
+ */
+int rpmb_get_capacity(struct rpmb_dev *rdev)
+{
+	int err;
+
+	if (!rdev)
+		return -EINVAL;
+
+	mutex_lock(&rdev->lock);
+	err = -EOPNOTSUPP;
+	if (rdev->ops && rdev->ops->get_capacity)
+		err = rdev->ops->get_capacity(rdev->dev.parent, rdev->target);
+	mutex_unlock(&rdev->lock);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(rpmb_get_capacity);
+
+/**
+ * rpmb_get_write_count() - returns the write counter of the rpmb device
+ * @rdev: rpmb device
+ *
+ * Return:
+ * *        counter
+ * *        -EINVAL on wrong parameters
+ * *        -EOPNOTSUPP if device doesn't support the requested operation
+ * *        < 0 if the operation fails
+ */
+int rpmb_get_write_count(struct rpmb_dev *rdev)
+{
+	int err;
+
+	if (!rdev)
+		return -EINVAL;
+
+	mutex_lock(&rdev->lock);
+	err = -EOPNOTSUPP;
+	if (rdev->ops && rdev->ops->get_write_count)
+		err = rdev->ops->get_write_count(rdev->dev.parent, rdev->target);
+	mutex_unlock(&rdev->lock);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(rpmb_get_write_count);
+
+/**
+ * rpmb_write_blocks() - write data to RPMB device
+ * @rdev: rpmb device
+ * @addr: block address (index of first block - 256B blocks)
+ * @count: number of 256B blosks
+ * @data: pointer to data to program
+ *
+ * Write a series of blocks to the RPMB device.
+ *
+ * Return:
+ * *        0 on success
+ * *        -EINVAL on wrong parameters
+ * *        -EACCESS no key set
+ * *        -EOPNOTSUPP if device doesn't support the requested operation
+ * *        < 0 if the operation fails
+ */
+int rpmb_write_blocks(struct rpmb_dev *rdev, key_serial_t keyid, int addr,
+		      int count, u8 *data)
+{
+	int err;
+
+	if (!rdev || !count || !data)
+		return -EINVAL;
+
+	mutex_lock(&rdev->lock);
+	err = -EOPNOTSUPP;
+	if (rdev->ops && rdev->ops->write_blocks) {
+		err = rdev->ops->write_blocks(rdev->dev.parent, rdev->target, keyid,
+					      addr, count, data);
+	}
+	mutex_unlock(&rdev->lock);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(rpmb_write_blocks);
+
+/**
+ * rpmb_read_blocks() - read data from RPMB device
+ * @rdev: rpmb device
+ * @addr: block address (index of first block - 256B blocks)
+ * @count: number of 256B blocks
+ * @data: pointer to data to read
+ *
+ * Read a series of one or more blocks from the RPMB device.
+ *
+ * Return:
+ * *        0 on success
+ * *        -EINVAL on wrong parameters
+ * *        -EACCESS no key set
+ * *        -EOPNOTSUPP if device doesn't support the requested operation
+ * *        < 0 if the operation fails
+ */
+int rpmb_read_blocks(struct rpmb_dev *rdev, int addr, int count, u8 *data)
+{
+	int err;
+
+	if (!rdev || !count || !data)
+		return -EINVAL;
+
+	mutex_lock(&rdev->lock);
+	err = -EOPNOTSUPP;
+	if (rdev->ops && rdev->ops->read_blocks) {
+		err = rdev->ops->read_blocks(rdev->dev.parent, rdev->target,
+					     addr, count, data);
+	}
+	mutex_unlock(&rdev->lock);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(rpmb_read_blocks);
+
+
+static void rpmb_dev_release(struct device *dev)
+{
+	struct rpmb_dev *rdev = to_rpmb_dev(dev);
+
+	ida_simple_remove(&rpmb_ida, rdev->id);
+	kfree(rdev);
+}
+
+struct class rpmb_class = {
+	.name = "rpmb",
+	.owner = THIS_MODULE,
+	.dev_release = rpmb_dev_release,
+};
+EXPORT_SYMBOL(rpmb_class);
+
+/**
+ * rpmb_dev_find_device() - return first matching rpmb device
+ * @data: data for the match function
+ * @match: the matching function
+ *
+ * Return: matching rpmb device or NULL on failure
+ */
+static
+struct rpmb_dev *rpmb_dev_find_device(const void *data,
+				      int (*match)(struct device *dev,
+						   const void *data))
+{
+	struct device *dev;
+
+	dev = class_find_device(&rpmb_class, NULL, data, match);
+
+	return dev ? to_rpmb_dev(dev) : NULL;
+}
+
+struct device_with_target {
+	const struct device *dev;
+	u8 target;
+};
+
+static int match_by_parent(struct device *dev, const void *data)
+{
+	const struct device_with_target *d = data;
+	struct rpmb_dev *rdev = to_rpmb_dev(dev);
+
+	return (d->dev && dev->parent == d->dev && rdev->target == d->target);
+}
+
+/**
+ * rpmb_dev_find_by_device() - retrieve rpmb device from the parent device
+ * @parent: parent device of the rpmb device
+ * @target: RPMB target/region within the physical device
+ *
+ * Return: NULL if there is no rpmb device associated with the parent device
+ */
+struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target)
+{
+	struct device_with_target t;
+
+	if (!parent)
+		return NULL;
+
+	t.dev = parent;
+	t.target = target;
+
+	return rpmb_dev_find_device(&t, match_by_parent);
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_find_by_device);
+
+/**
+ * rpmb_dev_unregister() - unregister RPMB partition from the RPMB subsystem
+ * @rdev: the rpmb device to unregister
+ * Return:
+ * *        0 on success
+ * *        -EINVAL on wrong parameters
+ */
+int rpmb_dev_unregister(struct rpmb_dev *rdev)
+{
+	if (!rdev)
+		return -EINVAL;
+
+	mutex_lock(&rdev->lock);
+	device_del(&rdev->dev);
+	mutex_unlock(&rdev->lock);
+
+	rpmb_dev_put(rdev);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_unregister);
+
+/**
+ * rpmb_dev_unregister_by_device() - unregister RPMB partition
+ *     from the RPMB subsystem
+ * @dev: the parent device of the rpmb device
+ * @target: RPMB target/region within the physical device
+ * Return:
+ * *        0 on success
+ * *        -EINVAL on wrong parameters
+ * *        -ENODEV if a device cannot be find.
+ */
+int rpmb_dev_unregister_by_device(struct device *dev, u8 target)
+{
+	struct rpmb_dev *rdev;
+
+	if (!dev)
+		return -EINVAL;
+
+	rdev = rpmb_dev_find_by_device(dev, target);
+	if (!rdev) {
+		dev_warn(dev, "no disk found %s\n", dev_name(dev->parent));
+		return -ENODEV;
+	}
+
+	rpmb_dev_put(rdev);
+
+	return rpmb_dev_unregister(rdev);
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_unregister_by_device);
+
+/**
+ * rpmb_dev_get_drvdata() - driver data getter
+ * @rdev: rpmb device
+ *
+ * Return: driver private data
+ */
+void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev)
+{
+	return dev_get_drvdata(&rdev->dev);
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_get_drvdata);
+
+/**
+ * rpmb_dev_set_drvdata() - driver data setter
+ * @rdev: rpmb device
+ * @data: data to store
+ */
+void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data)
+{
+	dev_set_drvdata(&rdev->dev, data);
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_set_drvdata);
+
+/**
+ * rpmb_dev_register - register RPMB partition with the RPMB subsystem
+ * @dev: storage device of the rpmb device
+ * @target: RPMB target/region within the physical device
+ * @ops: device specific operations
+ *
+ * Return: a pointer to rpmb device
+ */
+struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target,
+				   const struct rpmb_ops *ops)
+{
+	struct rpmb_dev *rdev;
+	int id;
+	int ret;
+
+	if (!dev || !ops)
+		return ERR_PTR(-EINVAL);
+
+	if (!ops->program_key)
+		return ERR_PTR(-EINVAL);
+
+	if (!ops->get_capacity)
+		return ERR_PTR(-EINVAL);
+
+	if (!ops->get_write_count)
+		return ERR_PTR(-EINVAL);
+
+	if (!ops->write_blocks)
+		return ERR_PTR(-EINVAL);
+
+	if (!ops->read_blocks)
+		return ERR_PTR(-EINVAL);
+
+	if (ops->type == RPMB_TYPE_ANY || ops->type > RPMB_TYPE_MAX)
+		return ERR_PTR(-EINVAL);
+
+	rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
+	if (!rdev)
+		return ERR_PTR(-ENOMEM);
+
+	id = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL);
+	if (id < 0) {
+		ret = id;
+		goto exit;
+	}
+
+	mutex_init(&rdev->lock);
+	rdev->ops = ops;
+	rdev->id = id;
+	rdev->target = target;
+
+	dev_set_name(&rdev->dev, "rpmb%d", id);
+	rdev->dev.class = &rpmb_class;
+	rdev->dev.parent = dev;
+	ret = device_register(&rdev->dev);
+	if (ret)
+		goto exit;
+
+	dev_dbg(&rdev->dev, "registered device\n");
+
+	return rdev;
+
+exit:
+	if (id >= 0)
+		ida_simple_remove(&rpmb_ida, id);
+	kfree(rdev);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(rpmb_dev_register);
+
+static int __init rpmb_init(void)
+{
+	ida_init(&rpmb_ida);
+	class_register(&rpmb_class);
+	return 0;
+}
+
+static void __exit rpmb_exit(void)
+{
+	class_unregister(&rpmb_class);
+	ida_destroy(&rpmb_ida);
+}
+
+subsys_initcall(rpmb_init);
+module_exit(rpmb_exit);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("RPMB class");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h
new file mode 100644
index 000000000000..718ba7c91ecd
--- /dev/null
+++ b/include/linux/rpmb.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/*
+ * Copyright (C) 2015-2019 Intel Corp. All rights reserved
+ * Copyright (C) 2021 Linaro Ltd
+ */
+#ifndef __RPMB_H__
+#define __RPMB_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/kref.h>
+#include <linux/key.h>
+
+/**
+ * struct rpmb_ops - RPMB ops to be implemented by underlying block device
+ *
+ * @program_key    : program device key (once only op).
+ * @get_capacity   : rpmb size in 128K units in for region/target.
+ * @get_write_count: return the device write counter
+ * @write_blocks   : write blocks to RPMB device
+ * @read_blocks    : read blocks from RPMB device
+ * @block_size     : block size in half sectors (1 == 256B)
+ * @wr_cnt_max     : maximal number of blocks that can be
+ *                   written in one access.
+ * @rd_cnt_max     : maximal number of blocks that can be
+ *                   read in one access.
+ * @auth_method    : rpmb_auth_method
+ * @dev_id         : unique device identifier
+ * @dev_id_len     : unique device identifier length
+ */
+struct rpmb_ops {
+	int (*program_key)(struct device *dev, u8 target, key_serial_t keyid);
+	int (*get_capacity)(struct device *dev, u8 target);
+	int (*get_write_count)(struct device *dev, u8 target);
+	int (*write_blocks)(struct device *dev, u8 target, key_serial_t keyid,
+			    int addr, int count, u8 *data);
+	int (*read_blocks)(struct device *dev, u8 target,
+			   int addr, int count, u8 *data);
+	u16 block_size;
+	u16 wr_cnt_max;
+	u16 rd_cnt_max;
+	u16 auth_method;
+	const u8 *dev_id;
+	size_t dev_id_len;
+};
+
+/**
+ * struct rpmb_dev - device which can support RPMB partition
+ *
+ * @lock       : the device lock
+ * @dev        : device
+ * @id         : device id
+ * @target     : RPMB target/region within the physical device
+ * @ops        : operation exported by block layer
+ */
+struct rpmb_dev {
+	struct mutex lock; /* device serialization lock */
+	struct device dev;
+	int id;
+	u8 target;
+	const struct rpmb_ops *ops;
+};
+
+#define to_rpmb_dev(x) container_of((x), struct rpmb_dev, dev)
+
+#if IS_ENABLED(CONFIG_RPMB)
+struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev);
+void rpmb_dev_put(struct rpmb_dev *rdev);
+struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target);
+struct rpmb_dev *rpmb_dev_get_by_type(u32 type);
+struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target,
+				   const struct rpmb_ops *ops);
+void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev);
+void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data);
+int rpmb_dev_unregister(struct rpmb_dev *rdev);
+int rpmb_dev_unregister_by_device(struct device *dev, u8 target);
+int rpmb_program_key(struct rpmb_dev *rdev, key_serial_t keyid);
+int rpmb_get_capacity(struct rpmb_dev *rdev);
+int rpmb_get_write_count(struct rpmb_dev *rdev);
+int rpmb_write_blocks(struct rpmb_dev *rdev, key_serial_t keyid,
+		      int addr, int count, u8 *data);
+int rpmb_read_blocks(struct rpmb_dev *rdev, int addr, int count, u8 *data);
+
+#else
+static inline struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
+{
+	return NULL;
+}
+
+static inline void rpmb_dev_put(struct rpmb_dev *rdev) { }
+
+static inline struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent,
+						       u8 target)
+{
+	return NULL;
+}
+
+static inline
+struct rpmb_dev *rpmb_dev_get_by_type(enum rpmb_type type)
+{
+	return NULL;
+}
+
+static inline void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev)
+{
+	return NULL;
+}
+
+static inline void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data)
+{
+}
+
+static inline struct rpmb_dev *
+rpmb_dev_register(struct device *dev, u8 target, const struct rpmb_ops *ops)
+{
+	return NULL;
+}
+
+static inline int rpmb_dev_unregister(struct rpmb_dev *dev)
+{
+	return 0;
+}
+
+static inline int rpmb_dev_unregister_by_device(struct device *dev, u8 target)
+{
+	return 0;
+}
+
+static inline int rpmb_program_key(struct rpmb_dev *rdev, key_serial_t keyid)
+{
+	return 0;
+}
+
+static inline rpmb_set_key(struct rpmb_dev *rdev, u8 *key, int keylen);
+{
+	return 0;
+}
+
+static inline int rpmb_get_capacity(struct rpmb_dev *rdev)
+{
+	return 0;
+}
+
+static inline int rpmb_get_write_count(struct rpmb_dev *rdev)
+{
+	return 0;
+}
+
+static inline int rpmb_write_blocks(struct rpmb_dev *rdev, int addr, int count,
+				    u8 *data)
+{
+	return 0;
+}
+
+static inline int rpmb_read_blocks(struct rpmb_dev *rdev, int addr, int count,
+				   u8 *data)
+{
+	return 0;
+}
+
+#endif /* CONFIG_RPMB */
+
+#endif /* __RPMB_H__ */
-- 
2.20.1


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

* [RFC PATCH  2/5] char: rpmb: provide a user space interface
  2021-03-03 13:54 [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend Alex Bennée
  2021-03-03 13:54 ` [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem Alex Bennée
@ 2021-03-03 13:54 ` Alex Bennée
  2021-03-04  7:01   ` Winkler, Tomas
  2021-03-04 21:08   ` Arnd Bergmann
  2021-03-03 13:54 ` [RFC PATCH 3/5] tools rpmb: add RPBM access tool Alex Bennée
                   ` (3 subsequent siblings)
  5 siblings, 2 replies; 47+ messages in thread
From: Alex Bennée @ 2021-03-03 13:54 UTC (permalink / raw)
  To: linux-kernel
  Cc: maxim.uvarov, joakim.bech, ilias.apalodimas, arnd, ruchika.gupta,
	tomas.winkler, yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc,
	linux-scsi, linux-nvme, Alex Bennée, Ulf Hansson,
	Linus Walleij, Arnd Bergmann, Alexander Usyskin, Avri Altman

The user space API is achieved via a number of synchronous IOCTLs.

  * RPMB_IOC_VER_CMD - simple versioning API
  * RPMB_IOC_CAP_CMD - query of underlying capabilities
  * RPMB_IOC_PKEY_CMD - one time programming of access key
  * RPMB_IOC_COUNTER_CMD - query the write counter
  * RPMB_IOC_WBLOCKS_CMD - write blocks to device
  * RPMB_IOC_RBLOCKS_CMD - read blocks from device

The keys used for programming and writing blocks to the device are
key_serial_t handles as provided by the keyctl() interface.

[AJB: here there are two key differences between this and the original
proposal. The first is the dropping of the sequence of preformated
frames in favour of explicit actions. The second is the introduction
of key_serial_t and the keyring API for referencing the key to use]

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Linus  Walleij <linus.walleij@linaro.org>
Cc: Arnd Bergmann <arnd.bergmann@linaro.org>
Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Cc: Tomas Winkler <tomas.winkler@intel.com>
Cc: Alexander Usyskin <alexander.usyskin@intel.com>
Cc: Avri Altman <avri.altman@sandisk.com>
---
 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 MAINTAINERS                                   |   1 +
 drivers/char/rpmb/Kconfig                     |   7 +
 drivers/char/rpmb/Makefile                    |   1 +
 drivers/char/rpmb/cdev.c                      | 246 ++++++++++++++++++
 drivers/char/rpmb/core.c                      |  10 +-
 drivers/char/rpmb/rpmb-cdev.h                 |  17 ++
 include/linux/rpmb.h                          |  10 +
 include/uapi/linux/rpmb.h                     |  68 +++++
 9 files changed, 357 insertions(+), 4 deletions(-)
 create mode 100644 drivers/char/rpmb/cdev.c
 create mode 100644 drivers/char/rpmb/rpmb-cdev.h
 create mode 100644 include/uapi/linux/rpmb.h

diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index a4c75a28c839..0ff2d4d81bb0 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -344,6 +344,7 @@ Code  Seq#    Include File                                           Comments
 0xB5  00-0F  uapi/linux/rpmsg.h                                      <mailto:linux-remoteproc@vger.kernel.org>
 0xB6  all    linux/fpga-dfl.h
 0xB7  all    uapi/linux/remoteproc_cdev.h                            <mailto:linux-remoteproc@vger.kernel.org>
+0xB8  80-8F  uapi/linux/rpmb.h                                       <mailto:linux-mmc@vger.kernel.org>
 0xC0  00-0F  linux/usb/iowarrior.h
 0xCA  00-0F  uapi/misc/cxl.h
 0xCA  10-2F  uapi/misc/ocxl.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 076f3983526c..c60b41b6e6bd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15374,6 +15374,7 @@ M:	?
 L:	linux-kernel@vger.kernel.org
 S:	Supported
 F:	drivers/char/rpmb/*
+F:	include/uapi/linux/rpmb.h
 F:	include/linux/rpmb.h
 
 RTL2830 MEDIA DRIVER
diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig
index 431c2823cf70..9068664a399a 100644
--- a/drivers/char/rpmb/Kconfig
+++ b/drivers/char/rpmb/Kconfig
@@ -9,3 +9,10 @@ config RPMB
 	  access RPMB partition.
 
 	  If unsure, select N.
+
+config RPMB_INTF_DEV
+	bool "RPMB character device interface /dev/rpmbN"
+	depends on RPMB && KEYS
+	help
+	  Say yes here if you want to access RPMB from user space
+	  via character device interface /dev/rpmb%d
diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile
index 24d4752a9a53..f54b3f30514b 100644
--- a/drivers/char/rpmb/Makefile
+++ b/drivers/char/rpmb/Makefile
@@ -3,5 +3,6 @@
 
 obj-$(CONFIG_RPMB) += rpmb.o
 rpmb-objs += core.o
+rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/char/rpmb/cdev.c b/drivers/char/rpmb/cdev.c
new file mode 100644
index 000000000000..55f66720fd03
--- /dev/null
+++ b/drivers/char/rpmb/cdev.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(c) 2015 - 2019 Intel Corporation.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/compat.h>
+#include <linux/slab.h>
+#include <linux/capability.h>
+
+#include <linux/rpmb.h>
+
+#include "rpmb-cdev.h"
+
+static dev_t rpmb_devt;
+#define RPMB_MAX_DEVS  MINORMASK
+
+#define RPMB_DEV_OPEN    0  /** single open bit (position) */
+
+/**
+ * rpmb_open - the open function
+ *
+ * @inode: pointer to inode structure
+ * @fp: pointer to file structure
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int rpmb_open(struct inode *inode, struct file *fp)
+{
+	struct rpmb_dev *rdev;
+
+	rdev = container_of(inode->i_cdev, struct rpmb_dev, cdev);
+	if (!rdev)
+		return -ENODEV;
+
+	/* the rpmb is single open! */
+	if (test_and_set_bit(RPMB_DEV_OPEN, &rdev->status))
+		return -EBUSY;
+
+	mutex_lock(&rdev->lock);
+
+	fp->private_data = rdev;
+
+	mutex_unlock(&rdev->lock);
+
+	return nonseekable_open(inode, fp);
+}
+
+/**
+ * rpmb_release - the cdev release function
+ *
+ * @inode: pointer to inode structure
+ * @fp: pointer to file structure
+ *
+ * Return: 0 always.
+ */
+static int rpmb_release(struct inode *inode, struct file *fp)
+{
+	struct rpmb_dev *rdev = fp->private_data;
+
+	clear_bit(RPMB_DEV_OPEN, &rdev->status);
+
+	return 0;
+}
+
+static long rpmb_ioctl_ver_cmd(struct rpmb_dev *rdev,
+			       struct rpmb_ioc_ver_cmd __user *ptr)
+{
+	struct rpmb_ioc_ver_cmd ver = {
+		.api_version = RPMB_API_VERSION,
+	};
+
+	return copy_to_user(ptr, &ver, sizeof(ver)) ? -EFAULT : 0;
+}
+
+static long rpmb_ioctl_cap_cmd(struct rpmb_dev *rdev,
+			       struct rpmb_ioc_cap_cmd __user *ptr)
+{
+	struct rpmb_ioc_cap_cmd cap;
+
+	cap.target      = rdev->target;
+	cap.block_size  = rdev->ops->block_size;
+	cap.wr_cnt_max  = rdev->ops->wr_cnt_max;
+	cap.rd_cnt_max  = rdev->ops->rd_cnt_max;
+	cap.auth_method = rdev->ops->auth_method;
+	cap.capacity    = rpmb_get_capacity(rdev);
+	cap.reserved    = 0;
+
+	return copy_to_user(ptr, &cap, sizeof(cap)) ? -EFAULT : 0;
+}
+
+static long rpmb_ioctl_pkey_cmd(struct rpmb_dev *rdev, key_serial_t __user *k)
+{
+	key_serial_t keyid;
+
+	if (get_user(keyid, k))
+		return -EFAULT;
+	else
+		return rpmb_program_key(rdev, keyid);
+}
+
+static long rpmb_ioctl_counter_cmd(struct rpmb_dev *rdev, int __user *ptr)
+{
+	int count = rpmb_get_write_count(rdev);
+
+	if (count > 0)
+		return put_user(count, ptr);
+	else
+		return count;
+}
+
+static long rpmb_ioctl_wblocks_cmd(struct rpmb_dev *rdev,
+				   struct rpmb_ioc_blocks_cmd __user *ptr)
+{
+	struct rpmb_ioc_blocks_cmd wblocks;
+	int sz;
+	long ret;
+	u8 *data;
+
+	if (copy_from_user(&wblocks, ptr, sizeof(struct rpmb_ioc_blocks_cmd)))
+		return -EFAULT;
+
+	/* Don't write more blocks device supports */
+	if (wblocks.count > rdev->ops->wr_cnt_max)
+		return -EINVAL;
+
+	sz = wblocks.count * 256;
+	data = kmalloc(sz, GFP_KERNEL);
+
+	if (!data)
+		return -ENOMEM;
+
+	if (copy_from_user(data, wblocks.data, sz))
+		ret = -EFAULT;
+	else
+		ret = rpmb_write_blocks(rdev, wblocks.key, wblocks.addr, wblocks.count, data);
+
+	kfree(data);
+	return ret;
+}
+
+static long rpmb_ioctl_rblocks_cmd(struct rpmb_dev *rdev,
+				   struct rpmb_ioc_blocks_cmd __user *ptr)
+{
+	struct rpmb_ioc_blocks_cmd rblocks;
+	int sz;
+	long ret;
+	u8 *data;
+
+	if (copy_from_user(&rblocks, ptr, sizeof(struct rpmb_ioc_blocks_cmd)))
+		return -EFAULT;
+
+	if (rblocks.count > rdev->ops->rd_cnt_max)
+		return -EINVAL;
+
+	sz = rblocks.count * 256;
+	data = kmalloc(sz, GFP_KERNEL);
+
+	if (!data)
+		return -ENOMEM;
+
+	ret = rpmb_read_blocks(rdev, rblocks.addr, rblocks.count, data);
+
+	if (ret == 0)
+		ret = copy_to_user(rblocks.data, data, sz);
+
+	kfree(data);
+	return ret;
+}
+
+/**
+ * rpmb_ioctl - rpmb ioctl dispatcher
+ *
+ * @fp: a file pointer
+ * @cmd: ioctl command RPMB_IOC_SEQ_CMD RPMB_IOC_VER_CMD RPMB_IOC_CAP_CMD
+ * @arg: ioctl data: rpmb_ioc_ver_cmd rpmb_ioc_cap_cmd pmb_ioc_seq_cmd
+ *
+ * Return: 0 on success; < 0 on error
+ */
+static long rpmb_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+	struct rpmb_dev *rdev = fp->private_data;
+	void __user *ptr = (void __user *)arg;
+
+	switch (cmd) {
+	case RPMB_IOC_VER_CMD:
+		return rpmb_ioctl_ver_cmd(rdev, ptr);
+	case RPMB_IOC_CAP_CMD:
+		return rpmb_ioctl_cap_cmd(rdev, ptr);
+	case RPMB_IOC_PKEY_CMD:
+		return rpmb_ioctl_pkey_cmd(rdev, ptr);
+	case RPMB_IOC_COUNTER_CMD:
+		return rpmb_ioctl_counter_cmd(rdev, ptr);
+	case RPMB_IOC_WBLOCKS_CMD:
+		return rpmb_ioctl_wblocks_cmd(rdev, ptr);
+	case RPMB_IOC_RBLOCKS_CMD:
+		return rpmb_ioctl_rblocks_cmd(rdev, ptr);
+	default:
+		dev_err(&rdev->dev, "unsupported ioctl 0x%x.\n", cmd);
+		return -ENOIOCTLCMD;
+	}
+}
+
+static const struct file_operations rpmb_fops = {
+	.open           = rpmb_open,
+	.release        = rpmb_release,
+	.unlocked_ioctl = rpmb_ioctl,
+	.owner          = THIS_MODULE,
+	.llseek         = noop_llseek,
+};
+
+void rpmb_cdev_prepare(struct rpmb_dev *rdev)
+{
+	rdev->dev.devt = MKDEV(MAJOR(rpmb_devt), rdev->id);
+	rdev->cdev.owner = THIS_MODULE;
+	cdev_init(&rdev->cdev, &rpmb_fops);
+}
+
+void rpmb_cdev_add(struct rpmb_dev *rdev)
+{
+	cdev_add(&rdev->cdev, rdev->dev.devt, 1);
+}
+
+void rpmb_cdev_del(struct rpmb_dev *rdev)
+{
+	if (rdev->dev.devt)
+		cdev_del(&rdev->cdev);
+}
+
+int __init rpmb_cdev_init(void)
+{
+	int ret;
+
+	ret = alloc_chrdev_region(&rpmb_devt, 0, RPMB_MAX_DEVS, "rpmb");
+	if (ret < 0)
+		pr_err("unable to allocate char dev region\n");
+
+	return ret;
+}
+
+void __exit rpmb_cdev_exit(void)
+{
+	unregister_chrdev_region(rpmb_devt, RPMB_MAX_DEVS);
+}
diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c
index a2e21c14986a..e26d605e48e1 100644
--- a/drivers/char/rpmb/core.c
+++ b/drivers/char/rpmb/core.c
@@ -12,6 +12,7 @@
 #include <linux/slab.h>
 
 #include <linux/rpmb.h>
+#include "rpmb-cdev.h"
 
 static DEFINE_IDA(rpmb_ida);
 
@@ -277,6 +278,7 @@ int rpmb_dev_unregister(struct rpmb_dev *rdev)
 		return -EINVAL;
 
 	mutex_lock(&rdev->lock);
+	rpmb_cdev_del(rdev);
 	device_del(&rdev->dev);
 	mutex_unlock(&rdev->lock);
 
@@ -371,9 +373,6 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target,
 	if (!ops->read_blocks)
 		return ERR_PTR(-EINVAL);
 
-	if (ops->type == RPMB_TYPE_ANY || ops->type > RPMB_TYPE_MAX)
-		return ERR_PTR(-EINVAL);
-
 	rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
 	if (!rdev)
 		return ERR_PTR(-ENOMEM);
@@ -396,6 +395,8 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target,
 	if (ret)
 		goto exit;
 
+	rpmb_cdev_add(rdev);
+
 	dev_dbg(&rdev->dev, "registered device\n");
 
 	return rdev;
@@ -412,11 +413,12 @@ static int __init rpmb_init(void)
 {
 	ida_init(&rpmb_ida);
 	class_register(&rpmb_class);
-	return 0;
+	return rpmb_cdev_init();
 }
 
 static void __exit rpmb_exit(void)
 {
+	rpmb_cdev_exit();
 	class_unregister(&rpmb_class);
 	ida_destroy(&rpmb_ida);
 }
diff --git a/drivers/char/rpmb/rpmb-cdev.h b/drivers/char/rpmb/rpmb-cdev.h
new file mode 100644
index 000000000000..e59ff0c05e9d
--- /dev/null
+++ b/drivers/char/rpmb/rpmb-cdev.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/*
+ * Copyright (C) 2015-2018 Intel Corp. All rights reserved
+ */
+#ifdef CONFIG_RPMB_INTF_DEV
+int __init rpmb_cdev_init(void);
+void __exit rpmb_cdev_exit(void);
+void rpmb_cdev_prepare(struct rpmb_dev *rdev);
+void rpmb_cdev_add(struct rpmb_dev *rdev);
+void rpmb_cdev_del(struct rpmb_dev *rdev);
+#else
+static inline int __init rpmb_cdev_init(void) { return 0; }
+static inline void __exit rpmb_cdev_exit(void) {}
+static inline void rpmb_cdev_prepare(struct rpmb_dev *rdev) {}
+static inline void rpmb_cdev_add(struct rpmb_dev *rdev) {}
+static inline void rpmb_cdev_del(struct rpmb_dev *rdev) {}
+#endif /* CONFIG_RPMB_INTF_DEV */
diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h
index 718ba7c91ecd..fe44f60efe31 100644
--- a/include/linux/rpmb.h
+++ b/include/linux/rpmb.h
@@ -8,9 +8,13 @@
 
 #include <linux/types.h>
 #include <linux/device.h>
+#include <linux/cdev.h>
+#include <uapi/linux/rpmb.h>
 #include <linux/kref.h>
 #include <linux/key.h>
 
+#define RPMB_API_VERSION 0x80000001
+
 /**
  * struct rpmb_ops - RPMB ops to be implemented by underlying block device
  *
@@ -51,6 +55,8 @@ struct rpmb_ops {
  * @dev        : device
  * @id         : device id
  * @target     : RPMB target/region within the physical device
+ * @cdev       : character dev
+ * @status     : device status
  * @ops        : operation exported by block layer
  */
 struct rpmb_dev {
@@ -58,6 +64,10 @@ struct rpmb_dev {
 	struct device dev;
 	int id;
 	u8 target;
+#ifdef CONFIG_RPMB_INTF_DEV
+	struct cdev cdev;
+	unsigned long status;
+#endif /* CONFIG_RPMB_INTF_DEV */
 	const struct rpmb_ops *ops;
 };
 
diff --git a/include/uapi/linux/rpmb.h b/include/uapi/linux/rpmb.h
new file mode 100644
index 000000000000..3957b785cdd5
--- /dev/null
+++ b/include/uapi/linux/rpmb.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/*
+ * Copyright (C) 2015-2018 Intel Corp. All rights reserved
+ * Copyright (C) 2021 Linaro Ltd
+ */
+#ifndef _UAPI_LINUX_RPMB_H_
+#define _UAPI_LINUX_RPMB_H_
+
+#include <linux/types.h>
+
+/**
+ * struct rpmb_ioc_ver_cmd - rpmb api version
+ *
+ * @api_version: rpmb API version.
+ */
+struct rpmb_ioc_ver_cmd {
+	__u32 api_version;
+} __packed;
+
+enum rpmb_auth_method {
+	RPMB_HMAC_ALGO_SHA_256 = 0,
+};
+
+/**
+ * struct rpmb_ioc_cap_cmd - rpmb capabilities
+ *
+ * @target: rpmb target/region within RPMB partition.
+ * @capacity: storage capacity (in units of 128K)
+ * @block_size: storage data block size (in units of 256B)
+ * @wr_cnt_max: maximal number of block that can be written in a single request.
+ * @rd_cnt_max: maximal number of block that can be read in a single request.
+ * @auth_method: authentication method: currently always HMAC_SHA_256
+ * @reserved: reserved to align to 4 bytes.
+ */
+struct rpmb_ioc_cap_cmd {
+	__u16 target;
+	__u16 capacity;
+	__u16 block_size;
+	__u16 wr_cnt_max;
+	__u16 rd_cnt_max;
+	__u16 auth_method;
+	__u16 reserved;
+} __attribute__((packed));
+
+/**
+ * struct rpmb_ioc_blocks_cmd - read/write blocks to/from RPMB
+ *
+ * @keyid: key_serial_t of key to use
+ * @addr: index into device (units of 256B blocks)
+ * @count: number of 256B blocks
+ * @data: pointer to data to write/read
+ */
+struct rpmb_ioc_blocks_cmd {
+	__s32 key; /* key_serial_t */
+	__u32 addr;
+	__u32 count;
+	__u8 __user *data;
+} __attribute__((packed));
+
+
+#define RPMB_IOC_VER_CMD     _IOR(0xB8, 80, struct rpmb_ioc_ver_cmd)
+#define RPMB_IOC_CAP_CMD     _IOR(0xB8, 81, struct rpmb_ioc_cap_cmd)
+#define RPMB_IOC_PKEY_CMD    _IOW(0xB8, 82, key_serial_t)
+#define RPMB_IOC_COUNTER_CMD _IOR(0xB8, 84, int)
+#define RPMB_IOC_WBLOCKS_CMD _IOW(0xB8, 85, struct rpmb_ioc_blocks_cmd)
+#define RPMB_IOC_RBLOCKS_CMD _IOR(0xB8, 86, struct rpmb_ioc_blocks_cmd)
+
+#endif /* _UAPI_LINUX_RPMB_H_ */
-- 
2.20.1


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

* [RFC PATCH  3/5] tools rpmb: add RPBM access tool
  2021-03-03 13:54 [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend Alex Bennée
  2021-03-03 13:54 ` [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem Alex Bennée
  2021-03-03 13:54 ` [RFC PATCH 2/5] char: rpmb: provide a user space interface Alex Bennée
@ 2021-03-03 13:54 ` Alex Bennée
  2021-03-03 13:54 ` [RFC PATCH 4/5] rpmb: create virtio rpmb frontend driver [WIP] Alex Bennée
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 47+ messages in thread
From: Alex Bennée @ 2021-03-03 13:54 UTC (permalink / raw)
  To: linux-kernel
  Cc: maxim.uvarov, joakim.bech, ilias.apalodimas, arnd, ruchika.gupta,
	tomas.winkler, yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc,
	linux-scsi, linux-nvme, Alex Bennée, Alexander Usyskin

Add simple RPMB host testing tool. It can be used to program key,
write and read data block, and retrieve write counter.

[AJB: the principle differences are a simpler ioctl API which doesn't
need to do the low level frame construction itself. It also uses the
kernel keychain API for managing the keys]

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Cc: Tomas Winkler <tomas.winkler@intel.com>
Cc: Alexander Usyskin <alexander.usyskin@intel.com>
---
 MAINTAINERS           |   1 +
 tools/Makefile        |  14 +-
 tools/rpmb/.gitignore |   2 +
 tools/rpmb/Makefile   |  41 +++
 tools/rpmb/rpmb.c     | 649 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 702 insertions(+), 5 deletions(-)
 create mode 100644 tools/rpmb/.gitignore
 create mode 100644 tools/rpmb/Makefile
 create mode 100644 tools/rpmb/rpmb.c

diff --git a/MAINTAINERS b/MAINTAINERS
index c60b41b6e6bd..8b0768b16eae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15376,6 +15376,7 @@ S:	Supported
 F:	drivers/char/rpmb/*
 F:	include/uapi/linux/rpmb.h
 F:	include/linux/rpmb.h
+F:	tools/rpmb/
 
 RTL2830 MEDIA DRIVER
 M:	Antti Palosaari <crope@iki.fi>
diff --git a/tools/Makefile b/tools/Makefile
index 85af6ebbce91..916ab8f8cefc 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -27,6 +27,7 @@ help:
 	@echo '  objtool                - an ELF object analysis tool'
 	@echo '  pci                    - PCI tools'
 	@echo '  perf                   - Linux performance measurement and analysis tool'
+	@echo '  rpmb                   - Replay protected memory block access tool'
 	@echo '  selftests              - various kernel selftests'
 	@echo '  bootconfig             - boot config tool'
 	@echo '  spi                    - spi tools'
@@ -64,7 +65,7 @@ acpi: FORCE
 cpupower: FORCE
 	$(call descend,power/$@)
 
-cgroup firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging: FORCE
+cgroup firewire hv guest bootconfig rpmb spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging: FORCE
 	$(call descend,$@)
 
 bpf/%: FORCE
@@ -100,7 +101,7 @@ kvm_stat: FORCE
 	$(call descend,kvm/$@)
 
 all: acpi cgroup cpupower gpio hv firewire liblockdep \
-		perf selftests bootconfig spi turbostat usb \
+		perf rpmb selftests bootconfig spi turbostat usb \
 		virtio vm bpf x86_energy_perf_policy \
 		tmon freefall iio objtool kvm_stat wmi \
 		pci debugging
@@ -111,7 +112,7 @@ acpi_install:
 cpupower_install:
 	$(call descend,power/$(@:_install=),install)
 
-cgroup_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install:
+cgroup_install firewire_install gpio_install hv_install iio_install rpmb_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install:
 	$(call descend,$(@:_install=),install)
 
 liblockdep_install:
@@ -134,7 +135,7 @@ kvm_stat_install:
 
 install: acpi_install cgroup_install cpupower_install gpio_install \
 		hv_install firewire_install iio_install liblockdep_install \
-		perf_install selftests_install turbostat_install usb_install \
+		perf_install rpmb_install selftests_install turbostat_install usb_install \
 		virtio_install vm_install bpf_install x86_energy_perf_policy_install \
 		tmon_install freefall_install objtool_install kvm_stat_install \
 		wmi_install pci_install debugging_install intel-speed-select_install
@@ -164,6 +165,9 @@ perf_clean:
 	$(Q)mkdir -p $(PERF_O) .
 	$(Q)$(MAKE) --no-print-directory -C perf O=$(PERF_O) subdir= clean
 
+rpmb_clean:
+	$(call descend,$(@:_clean=),clean)
+
 selftests_clean:
 	$(call descend,testing/$(@:_clean=),clean)
 
@@ -180,7 +184,7 @@ build_clean:
 	$(call descend,build,clean)
 
 clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
-		perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \
+		perf_clean rpmb_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \
 		vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
 		freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
 		gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean \
diff --git a/tools/rpmb/.gitignore b/tools/rpmb/.gitignore
new file mode 100644
index 000000000000..218f680548e6
--- /dev/null
+++ b/tools/rpmb/.gitignore
@@ -0,0 +1,2 @@
+*.o
+rpmb
diff --git a/tools/rpmb/Makefile b/tools/rpmb/Makefile
new file mode 100644
index 000000000000..3d49a94ffb66
--- /dev/null
+++ b/tools/rpmb/Makefile
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../scripts/Makefile.include
+
+CC ?= $(CROSS_COMPILE)gcc
+LD ?= $(CROSS_COMPILE)ld
+PKG_CONFIG = $(CROSS_COMPILE)pkg-config
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(shell pwd)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+#$(info Determined 'srctree' to be $(srctree))
+endif
+
+INSTALL = install
+prefix ?= /usr/local
+bindir = $(prefix)/bin
+
+
+CFLAGS += $(HOSTCFLAGS)
+CFLAGS += -D__EXPORTED_HEADERS__
+CFLAGS += -Wall -Wextra -ggdb
+ifdef RPMB_STATIC
+LDFLAGS += -pthread -static -Wl,-u,pthread_mutex_unlock
+CFLAGS +=  -pthread -static
+PKG_STATIC = --static
+endif
+CFLAGS += -I$(srctree)/include/uapi -I$(srctree)/include
+LDLIBS += $(shell $(PKG_CONFIG) --libs $(PKG_STATIC) libkeyutils)
+
+prog := rpmb
+
+all : $(prog)
+
+$(prog): rpmb.o
+
+clean :
+	$(RM) $(prog) *.o
+
+install: $(prog)
+	$(INSTALL) -m755 -d $(DESTDIR)$(bindir)
+	$(INSTALL) $(prog) $(DESTDIR)$(bindir)
diff --git a/tools/rpmb/rpmb.c b/tools/rpmb/rpmb.c
new file mode 100644
index 000000000000..d5b85af14f94
--- /dev/null
+++ b/tools/rpmb/rpmb.c
@@ -0,0 +1,649 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (C) 2016-2019 Intel Corp. All rights reserved
+ * Copyright (C) 2021 Linaro Ltd
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <keyutils.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "linux/rpmb.h"
+
+#define RPMB_KEY_SIZE 32
+#define RPMB_MAC_SIZE 32
+#define RPMB_NONCE_SIZE 16
+
+#define min(a,b)			\
+	({ __typeof__ (a) _a = (a);	\
+	   __typeof__ (b) _b = (b);	\
+		_a < _b ? _a : _b; })
+
+
+static bool verbose;
+#define rpmb_dbg(fmt, ARGS...) do {                     \
+	if (verbose)                                    \
+		fprintf(stderr, "rpmb: " fmt, ##ARGS);  \
+} while (0)
+
+#define rpmb_msg(fmt, ARGS...) \
+	fprintf(stderr, "rpmb: " fmt, ##ARGS)
+
+#define rpmb_err(fmt, ARGS...) \
+	fprintf(stderr, "rpmb: error: " fmt, ##ARGS)
+
+static int open_dev_file(const char *devfile, struct rpmb_ioc_cap_cmd *cap)
+{
+	struct rpmb_ioc_ver_cmd ver;
+	int fd;
+	int ret;
+
+	fd = open(devfile, O_RDWR);
+	if (fd < 0)
+		rpmb_err("Cannot open: %s: %s.\n", devfile, strerror(errno));
+
+	ret = ioctl(fd, RPMB_IOC_VER_CMD, &ver);
+	if (ret < 0) {
+		rpmb_err("ioctl failure %d: %s.\n", ret, strerror(errno));
+		goto err;
+	}
+
+	printf("RPMB API Version %X\n", ver.api_version);
+
+	ret = ioctl(fd, RPMB_IOC_CAP_CMD, cap);
+	if (ret < 0) {
+		rpmb_err("ioctl failure %d: %s.\n", ret, strerror(errno));
+		goto err;
+	}
+
+	rpmb_dbg("RPMB rpmb_target = %hd\n", cap->target);
+	rpmb_dbg("RPMB capacity    = %hd\n", cap->capacity);
+	rpmb_dbg("RPMB block_size  = %hd\n", cap->block_size);
+	rpmb_dbg("RPMB wr_cnt_max  = %hd\n", cap->wr_cnt_max);
+	rpmb_dbg("RPMB rd_cnt_max  = %hd\n", cap->rd_cnt_max);
+	rpmb_dbg("RPMB auth_method = %hd\n", cap->auth_method);
+
+	return fd;
+err:
+	close(fd);
+	return -1;
+}
+
+static int open_rd_file(const char *datafile, const char *type)
+{
+	int fd;
+
+	if (!strcmp(datafile, "-"))
+		fd = STDIN_FILENO;
+	else
+		fd = open(datafile, O_RDONLY);
+
+	if (fd < 0)
+		rpmb_err("Cannot open %s: %s: %s.\n",
+			 type, datafile, strerror(errno));
+
+	return fd;
+}
+
+static int open_wr_file(const char *datafile, const char *type)
+{
+	int fd;
+
+	if (!strcmp(datafile, "-"))
+		fd = STDOUT_FILENO;
+	else
+		fd = open(datafile, O_WRONLY | O_CREAT | O_APPEND, 0600);
+	if (fd < 0)
+		rpmb_err("Cannot open %s: %s: %s.\n",
+			 type, datafile, strerror(errno));
+	return fd;
+}
+
+static void close_fd(int fd)
+{
+	if (fd > 0 && fd != STDIN_FILENO && fd != STDOUT_FILENO)
+		close(fd);
+}
+
+/* need to just cast out 'const' in write(2) */
+typedef ssize_t (*rwfunc_t)(int fd, void *buf, size_t count);
+/* blocking rw wrapper */
+static ssize_t rw(rwfunc_t func, int fd, unsigned char *buf, size_t size)
+{
+	ssize_t ntotal = 0, n;
+	char *_buf = (char *)buf;
+
+	do {
+		n = func(fd, _buf + ntotal, size - ntotal);
+		if (n == -1 && errno != EINTR) {
+			ntotal = -1;
+			break;
+		} else if (n > 0) {
+			ntotal += n;
+		}
+	} while (n != 0 && (size_t)ntotal != size);
+
+	return ntotal;
+}
+
+static ssize_t read_file(int fd, unsigned char *data, size_t size)
+{
+	ssize_t ret;
+
+	ret = rw(read, fd, data, size);
+	if (ret < 0) {
+		rpmb_err("cannot read file: %s\n.", strerror(errno));
+	} else if ((size_t)ret != size) {
+		rpmb_err("read %zd but must be %zu bytes length.\n", ret, size);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static ssize_t write_file(int fd, unsigned char *data, size_t size)
+{
+	ssize_t ret;
+
+	ret = rw((rwfunc_t)write, fd, data, size);
+	if (ret < 0) {
+		rpmb_err("cannot read file: %s.\n", strerror(errno));
+	} else if ((size_t)ret != size) {
+		rpmb_err("data is %zd but must be %zu bytes length.\n",
+			 ret, size);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int op_get_info(int nargs, char *argv[])
+{
+	int dev_fd;
+	struct rpmb_ioc_cap_cmd cap;
+
+	if (nargs != 1)
+		return -EINVAL;
+
+	memset(&cap, 0, sizeof(cap));
+	dev_fd = open_dev_file(argv[0], &cap);
+	if (dev_fd < 0)
+		return -errno;
+	argv++;
+
+	printf("RPMB rpmb_target = %hd\n", cap.target);
+	printf("RPMB capacity    = %hd\n", cap.capacity);
+	printf("RPMB block_size  = %hd\n", cap.block_size);
+	printf("RPMB wr_cnt_max  = %hd\n", cap.wr_cnt_max);
+	printf("RPMB rd_cnt_max  = %hd\n", cap.rd_cnt_max);
+	printf("RPMB auth_method = %hd\n", cap.auth_method);
+
+	close(dev_fd);
+
+	return 0;
+}
+
+static int op_rpmb_add_key(int nargs, char *argv[])
+{
+	int key_fd, ret = -EINVAL;
+	unsigned char key_data[RPMB_KEY_SIZE];
+	key_serial_t key;
+
+	if (nargs != 1)
+		return -EINVAL;
+
+	key_fd = open_rd_file(argv[0], "key file");
+	if (key_fd < 0) {
+		perror("opening key file");
+		return ret;
+	}
+
+	if (read_file(key_fd, key_data, RPMB_KEY_SIZE) != RPMB_KEY_SIZE)
+	{
+		perror("reading key file");
+		return ret;
+	}
+
+	key = add_key("user", "RPMB MAC", key_data, RPMB_KEY_SIZE,
+		      KEY_SPEC_SESSION_KEYRING);
+
+	if (key == -1) {
+		perror("add_key");
+		return ret;
+	}
+
+	printf("Key ID is %jx\n", (uintmax_t) key);
+
+	return 0;
+}
+
+static key_serial_t get_key(char *keyid) {
+	key_serial_t key = -1;
+
+	if (keyid) {
+		if (sscanf(keyid, "%jx", (uintmax_t *) &key) != 1)
+			perror("reading keyid");
+	} else {
+		key = request_key("user", "RPMB MAC", NULL,
+				  KEY_SPEC_SESSION_KEYRING);
+	}
+	return key;
+}
+
+static int op_rpmb_program_key(int nargs, char *argv[])
+{
+	int ret, fd = -1;
+	key_serial_t key;
+	struct rpmb_ioc_cap_cmd cap;
+
+	ret = -EINVAL;
+	if (nargs < 1 || nargs > 2)
+		return ret;
+
+	fd = open_dev_file(argv[0], &cap);
+	if (fd < 0) {
+		perror("opening RPMB device");
+		return ret;
+	}
+	argv++;
+
+	key = get_key(nargs == 2 ? argv[0] : NULL);
+	if (key == -1)
+		goto out;
+
+	ret = ioctl(fd, RPMB_IOC_PKEY_CMD, &key);
+	if (ret < 0) {
+		rpmb_err("pkey ioctl failure %d: %s.\n", ret, strerror(errno));
+	}
+
+out:
+	close_fd(fd);
+	return ret;
+}
+
+
+static int op_rpmb_get_write_counter(int nargs, char **argv)
+{
+	int ret, fd = -1;
+	unsigned int counter;
+	struct rpmb_ioc_cap_cmd cap;
+
+	ret = -EINVAL;
+	if (nargs != 1)
+		return ret;
+
+	fd = open_dev_file(argv[0], &cap);
+	if (fd < 0) {
+		perror("opening RPMB device");
+		return ret;
+	}
+
+	ret = ioctl(fd, RPMB_IOC_COUNTER_CMD, &counter);
+	if (ret < 0) {
+		rpmb_err("counter ioctl failure %d: %s.\n", ret, strerror(errno));
+	}
+
+	printf("Counter value is: %ud\n", counter);
+
+	close_fd(fd);
+	return ret;
+}
+
+static int op_rpmb_read_blocks(int nargs, char **argv)
+{
+	int ret, data_fd, fd = -1;
+	struct rpmb_ioc_cap_cmd cap;
+	unsigned long numarg;
+	uint16_t addr, blocks_cnt;
+
+	ret = -EINVAL;
+	if (nargs != 4)
+		return ret;
+
+	fd = open_dev_file(argv[0], &cap);
+	if (fd < 0) {
+		perror("opening RPMB device");
+		return ret;
+	}
+	argv++;
+
+	errno = 0;
+	numarg = strtoul(argv[0], NULL, 0);
+	if (errno || numarg > USHRT_MAX) {
+		rpmb_err("wrong block address\n");
+		goto out;
+	}
+	addr = (uint16_t)numarg;
+	argv++;
+
+	errno = 0;
+	numarg = strtoul(argv[0], NULL, 0);
+	if (errno || numarg > USHRT_MAX) {
+		rpmb_err("wrong blocks count\n");
+		goto out;
+	}
+	blocks_cnt = (uint16_t)numarg;
+	argv++;
+
+	if (blocks_cnt == 0) {
+		rpmb_err("wrong blocks count\n");
+		goto out;
+	}
+
+	data_fd = open_wr_file(argv[0], "output data");
+	if (data_fd < 0)
+		goto out;
+	argv++;
+
+	while (blocks_cnt > 0) {
+		int to_copy = min(blocks_cnt, cap.rd_cnt_max);
+		int length = to_copy * 256;
+		void *data = malloc(length);
+		struct rpmb_ioc_blocks_cmd cmd;
+		if (!data) {
+			ret = ENOMEM;
+			goto out;
+		}
+		cmd.addr = addr;
+		cmd.count = to_copy;
+		cmd.data = data;
+
+		ret = ioctl(fd, RPMB_IOC_RBLOCKS_CMD, &cmd);
+		if (ret < 0) {
+			rpmb_err("rblocks ioctl failure %d: %s.\n", ret,
+				 strerror(errno));
+			goto out;
+		}
+
+		ret = write_file(data_fd, data, length);
+		if (ret < 0) {
+			perror("writing data");
+			goto out;
+		}
+
+		free(data);
+		addr += to_copy;
+		blocks_cnt -= to_copy;
+	}
+
+	ret = 0;
+out:
+	close_fd(fd);
+	close_fd(data_fd);
+
+	return ret;
+}
+
+static int op_rpmb_write_blocks(int nargs, char **argv)
+{
+	int ret, data_fd, fd = -1;
+	struct rpmb_ioc_cap_cmd cap;
+	unsigned long numarg;
+	uint16_t addr, blocks_cnt;
+	key_serial_t key;
+
+	ret = -EINVAL;
+	if (nargs < 4 || nargs > 5)
+		return ret;
+
+	fd = open_dev_file(argv[0], &cap);
+	if (fd < 0) {
+		perror("opening RPMB device");
+		return ret;
+	}
+	argv++;
+
+	errno = 0;
+	numarg = strtoul(argv[0], NULL, 0);
+	if (errno || numarg > USHRT_MAX) {
+		rpmb_err("wrong block address\n");
+		goto out;
+	}
+	addr = (uint16_t)numarg;
+	argv++;
+
+	errno = 0;
+	numarg = strtoul(argv[0], NULL, 0);
+	if (errno || numarg > USHRT_MAX) {
+		rpmb_err("wrong blocks count\n");
+		goto out;
+	}
+	blocks_cnt = (uint16_t)numarg;
+	argv++;
+
+	if (blocks_cnt == 0) {
+		rpmb_err("wrong blocks count\n");
+		goto out;
+	}
+
+	data_fd = open_wr_file(argv[0], "input data");
+	if (data_fd < 0)
+		goto out;
+	argv++;
+
+	key = get_key(nargs == 5 ? argv[0] : NULL);
+	if (key == -1)
+		goto out;
+
+	while (blocks_cnt > 0) {
+		int to_copy = min(blocks_cnt, cap.wr_cnt_max);
+		int length = to_copy * 256;
+		void *data = malloc(length);
+		struct rpmb_ioc_blocks_cmd cmd;
+		if (!data) {
+			ret = ENOMEM;
+			goto out;
+		}
+		cmd.key =
+		cmd.addr = addr;
+		cmd.count = to_copy;
+		cmd.data = data;
+
+		ret = read_file(data_fd, data, length);
+		if (ret < 0) {
+			perror("reading data");
+			goto out;
+		}
+
+		ret = ioctl(fd, RPMB_IOC_WBLOCKS_CMD, &cmd);
+		if (ret < 0) {
+			rpmb_err("wblocks ioctl failure %d: %s.\n", ret,
+				 strerror(errno));
+			goto out;
+		}
+
+		free(data);
+		addr += to_copy;
+		blocks_cnt -= to_copy;
+	}
+
+	ret = 0;
+out:
+	close_fd(fd);
+	close_fd(data_fd);
+
+	return ret;
+}
+
+typedef int (*rpmb_op)(int argc, char *argv[]);
+
+struct rpmb_cmd {
+	const char *op_name;
+	rpmb_op     op;
+	const char  *usage; /* usage title */
+	const char  *help;  /* help */
+};
+
+static const struct rpmb_cmd cmds[] = {
+	{
+		"get-info",
+		op_get_info,
+		"<RPMB_DEVICE>",
+		"    Get RPMB device info\n",
+	},
+	{
+		"add-key",
+		op_rpmb_add_key,
+		"<KEY_FILE>",
+		"    Load a 32 byte KEY_FILE into the session keyring.\n"
+		"    Returns a KEYID to use in future transactions.",
+	},
+	{
+		"program-key",
+		op_rpmb_program_key,
+		"<RPMB_DEVICE> <KEYID>",
+		"    Program authentication KEYID\n"
+		"    NOTE: This is a one-time programmable irreversible change.\n",
+	},
+	{
+		"write-counter",
+		op_rpmb_get_write_counter,
+		"<RPMB_DEVICE>",
+		"    Rertrive write counter value from the <RPMB_DEVICE> to stdout.\n"
+	},
+	{
+		"write-blocks",
+		op_rpmb_write_blocks,
+		"<RPMB_DEVICE> <address> <block_count> <DATA_FILE> <KEYID>",
+		"    <block count> of 256 bytes will be written from the DATA_FILE\n"
+		"    to the <RPMB_DEVICE> at block offset <address>.\n"
+		"    When DATA_FILE is -, read from standard input.\n",
+	},
+	{
+		"read-blocks",
+		op_rpmb_read_blocks,
+		"<RPMB_DEVICE> <address> <blocks count> <OUTPUT_FILE>",
+		"    <block count> of 256 bytes will be read from <RPMB_DEVICE>\n"
+		"    to the OUTPUT_FILE\n"
+		"    When OUTPUT_FILE is -, write to standard output\n",
+	},
+
+	{ NULL, NULL, NULL, NULL }
+};
+
+static void help(const char *prog, const struct rpmb_cmd *cmd)
+{
+	printf("%s %s %s\n", prog, cmd->op_name, cmd->usage);
+	printf("%s\n", cmd->help);
+}
+
+static void usage(const char *prog)
+{
+	int i;
+
+	printf("\n");
+	printf("Usage: %s [-v] <command> <args>\n\n", prog);
+	for (i = 0; cmds[i].op_name; i++)
+		printf("       %s %s %s\n",
+		       prog, cmds[i].op_name, cmds[i].usage);
+
+	printf("\n");
+	printf("      %s -v/--verbose: runs in verbose mode\n", prog);
+	printf("      %s help : shows this help\n", prog);
+	printf("      %s help <command>: shows detailed help\n", prog);
+}
+
+static bool call_for_help(const char *arg)
+{
+	return !strcmp(arg, "help") ||
+	       !strcmp(arg, "-h")   ||
+	       !strcmp(arg, "--help");
+}
+
+static bool parse_verbose(const char *arg)
+{
+	return !strcmp(arg, "-v") ||
+	       !strcmp(arg, "--verbose");
+}
+
+static const
+struct rpmb_cmd *parse_args(const char *prog, int *_argc, char **_argv[])
+{
+	int i;
+	int argc = *_argc;
+	char **argv =  *_argv;
+	const struct rpmb_cmd *cmd = NULL;
+	bool need_help = false;
+
+	argc--; argv++;
+
+	if (argc == 0)
+		goto out;
+
+	if (call_for_help(argv[0])) {
+		argc--; argv++;
+		if (argc == 0)
+			goto out;
+
+		need_help = true;
+	}
+
+	if (parse_verbose(argv[0])) {
+		argc--; argv++;
+		if (argc == 0)
+			goto out;
+
+		verbose = true;
+	}
+
+	for (i = 0; cmds[i].op_name; i++) {
+		if (!strncmp(argv[0], cmds[i].op_name,
+			     strlen(cmds[i].op_name))) {
+			cmd = &cmds[i];
+			argc--; argv++;
+			break;
+		}
+	}
+
+	if (!cmd)
+		goto out;
+
+	if (need_help || (argc > 0 && call_for_help(argv[0]))) {
+		help(prog, cmd);
+		argc--; argv++;
+		return NULL;
+	}
+
+out:
+	*_argc = argc;
+	*_argv = argv;
+
+	if (!cmd)
+		usage(prog);
+
+	return cmd;
+}
+
+int main(int argc, char *argv[])
+{
+	const char *prog = basename(argv[0]);
+	const struct rpmb_cmd *cmd;
+	int ret;
+
+	cmd = parse_args(prog, &argc, &argv);
+	if (!cmd)
+		exit(EXIT_SUCCESS);
+
+	ret = cmd->op(argc, argv);
+	if (ret == -EINVAL)
+		help(prog, cmd);
+
+	if (ret) {
+		exit(EXIT_FAILURE);
+	}
+
+	exit(EXIT_SUCCESS);
+}
-- 
2.20.1


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

* [RFC PATCH  4/5] rpmb: create virtio rpmb frontend driver [WIP]
  2021-03-03 13:54 [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend Alex Bennée
                   ` (2 preceding siblings ...)
  2021-03-03 13:54 ` [RFC PATCH 3/5] tools rpmb: add RPBM access tool Alex Bennée
@ 2021-03-03 13:54 ` Alex Bennée
  2021-03-03 13:55 ` [RFC PATCH 5/5] tools/rpmb: simple test sequence Alex Bennée
  2021-03-09 13:27 ` [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend Avri Altman
  5 siblings, 0 replies; 47+ messages in thread
From: Alex Bennée @ 2021-03-03 13:54 UTC (permalink / raw)
  To: linux-kernel
  Cc: maxim.uvarov, joakim.bech, ilias.apalodimas, arnd, ruchika.gupta,
	tomas.winkler, yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc,
	linux-scsi, linux-nvme, Alex Bennée

This implements a virtio rpmb frontend driver for the RPMB subsystem.

[AJB: the principle difference is all the MAC calculation and
validation is now done in the driver]

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Cc: Tomas Winkler <tomas.winkler@intel.com>

---
ajb:
  - add include linux/slab.h for kzalloc
  - include standardised VIRTIO_ID_RPMB
  - remove extra frames when sending commands down virtqueue
  - clean up out/in sgs - hopefully more conforming
  - remove cmd_cap code
---
 drivers/char/rpmb/Kconfig        |  10 +
 drivers/char/rpmb/Makefile       |   1 +
 drivers/char/rpmb/virtio_rpmb.c  | 366 +++++++++++++++++++++++++++++++
 include/uapi/linux/virtio_ids.h  |   1 +
 include/uapi/linux/virtio_rpmb.h |  54 +++++
 5 files changed, 432 insertions(+)
 create mode 100644 drivers/char/rpmb/virtio_rpmb.c
 create mode 100644 include/uapi/linux/virtio_rpmb.h

diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig
index 9068664a399a..845f458452a5 100644
--- a/drivers/char/rpmb/Kconfig
+++ b/drivers/char/rpmb/Kconfig
@@ -16,3 +16,13 @@ config RPMB_INTF_DEV
 	help
 	  Say yes here if you want to access RPMB from user space
 	  via character device interface /dev/rpmb%d
+
+config VIRTIO_RPMB
+	tristate "Virtio RPMB character device interface /dev/vrpmb"
+	default n
+	depends on VIRTIO && KEYS
+	select RPMB
+	help
+	  Say yes here if you want to access virtio RPMB from user space
+	  via character device interface /dev/vrpmb.
+	  This device interface is only for guest/frontend virtio driver.
diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile
index f54b3f30514b..4b397b50a42c 100644
--- a/drivers/char/rpmb/Makefile
+++ b/drivers/char/rpmb/Makefile
@@ -4,5 +4,6 @@
 obj-$(CONFIG_RPMB) += rpmb.o
 rpmb-objs += core.o
 rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o
+obj-$(CONFIG_VIRTIO_RPMB) += virtio_rpmb.o
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/char/rpmb/virtio_rpmb.c b/drivers/char/rpmb/virtio_rpmb.c
new file mode 100644
index 000000000000..6ade26874a4e
--- /dev/null
+++ b/drivers/char/rpmb/virtio_rpmb.c
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Virtio RPMB Front End Driver
+ *
+ * Copyright (c) 2018-2019 Intel Corporation.
+ * Copyright (c) 2021 Linaro Ltd.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/err.h>
+#include <linux/scatterlist.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/virtio.h>
+#include <linux/module.h>
+#include <linux/virtio_ids.h>
+#include <linux/fs.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_rpmb.h>
+#include <linux/uaccess.h>
+#include <linux/key.h>
+#include <linux/rpmb.h>
+
+#define RPMB_MAC_SIZE 32
+
+static const char id[] = "RPMB:VIRTIO";
+
+struct virtio_rpmb_info {
+	struct virtqueue *vq;
+	struct mutex lock; /* info lock */
+	wait_queue_head_t have_data;
+	struct rpmb_dev *rdev;
+};
+
+static void virtio_rpmb_recv_done(struct virtqueue *vq)
+{
+	struct virtio_rpmb_info *vi;
+	struct virtio_device *vdev = vq->vdev;
+
+	vi = vq->vdev->priv;
+	if (!vi) {
+		dev_err(&vdev->dev, "Error: no found vi data.\n");
+		return;
+	}
+
+	wake_up(&vi->have_data);
+}
+
+/* static int rpmb_virtio_cmd_seq(struct device *dev, u8 target, */
+/* 			       struct rpmb_cmd *cmds, u32 ncmds) */
+/* { */
+/* 	struct virtio_device *vdev = dev_to_virtio(dev); */
+/* 	struct virtio_rpmb_info *vi = vdev->priv; */
+/* 	unsigned int i; */
+/* 	struct scatterlist out_frames[3]; */
+/* 	struct scatterlist in_frames[3]; */
+/* 	struct scatterlist *sgs[3]; */
+/* 	unsigned int num_out = 0, num_in = 0; */
+/* 	size_t sz; */
+/* 	int ret; */
+/* 	unsigned int len; */
+
+/* 	if (ncmds > RPMB_SEQ_CMD_MAX) */
+/* 		return -EINVAL; */
+
+/* 	mutex_lock(&vi->lock); */
+
+/* 	/\* */
+/* 	 * Process the frames - putting in the appropriate out_frames */
+/* 	 * and in_frames before we build our final ordered sgs[] out */
+/* 	 * array. */
+/* 	 *\/ */
+/* 	for (i = 0; i < ncmds; i++) { */
+/* 		struct rpmb_cmd *cmd = &cmds[i]; */
+/* 		sz = sizeof(struct rpmb_frame_jdec) * (cmd->nframes ?: 1); */
+/* 		if (cmd->flags) { */
+/* 			sg_init_one(&out_frames[num_out++], cmd->frames, sz); */
+/* 		} else { */
+/* 			sg_init_one(&in_frames[num_in++], cmd->frames, sz); */
+/* 		} */
+/* 	} */
+
+/* 	for (i = 0; i < num_out; i++) { */
+/* 		sgs[i] = &out_frames[i]; */
+/* 	} */
+/* 	for (i = 0; i < num_in; i++) { */
+/* 		sgs[num_out + i] = &in_frames[i]; */
+/* 	} */
+
+/* 	dev_warn(dev, "out = %d, in = %d\n", num_out, num_in); */
+
+/* 	virtqueue_add_sgs(vi->vq, sgs, num_out, num_in, vi, GFP_KERNEL); */
+/* 	virtqueue_kick(vi->vq); */
+
+/* 	wait_event(vi->have_data, virtqueue_get_buf(vi->vq, &len)); */
+
+/* 	ret = 0; */
+
+/* 	/\* */
+/* 	 * We can't fail at this point, we assume we got a response */
+/* 	 * buffer back. */
+/* 	 *\/ */
+
+/* 	mutex_unlock(&vi->lock); */
+/* 	return ret; */
+/* } */
+
+static void * rpmb_virtio_get_key(struct device *dev, key_serial_t keyid)
+{
+	key_ref_t keyref;
+	struct key *key;
+	void *ptr = NULL;
+
+	keyref = lookup_user_key(keyid, 0, KEY_NEED_SEARCH);
+	if (IS_ERR(keyref))
+		return NULL;
+
+	key = key_ref_to_ptr(keyref);
+
+	if (key->datalen != RPMB_MAC_SIZE)
+		dev_err(dev, "Invalid key size (%d)", key->datalen);
+	else
+		ptr = key->payload.data[0];
+
+	key_put(key);
+	return ptr;
+}
+
+static int rpmb_virtio_program_key(struct device *dev, u8 target, key_serial_t keyid)
+{
+	struct virtio_device *vdev = dev_to_virtio(dev);
+	struct virtio_rpmb_info *vi = vdev->priv;
+	const void *key = rpmb_virtio_get_key(dev, keyid);
+
+	if (key) {
+		struct scatterlist out_frame;
+		struct scatterlist in_frame;
+		struct scatterlist *sgs[2];
+		unsigned int len;
+		struct virtio_rpmb_frame resp;
+		struct virtio_rpmb_frame cmd = {
+			.req_resp = cpu_to_le16(VIRTIO_RPMB_REQ_PROGRAM_KEY)
+		};
+
+		/* Prepare the command frame & response */
+		memcpy(&cmd.key_mac, key, sizeof(cmd.key_mac));
+
+		mutex_lock(&vi->lock);
+
+		/* Wrap into SG array */
+		sg_init_one(&out_frame, &cmd, sizeof(cmd));
+		sg_init_one(&in_frame, &resp, sizeof(resp));
+		sgs[0] = &out_frame;
+		sgs[1] = &in_frame;
+
+		/* Send it */
+		virtqueue_add_sgs(vi->vq, sgs, 1, 1, vi, GFP_KERNEL);
+		virtqueue_kick(vi->vq);
+		wait_event(vi->have_data, virtqueue_get_buf(vi->vq, &len));
+
+		mutex_unlock(&vi->lock);
+
+		if (le16_to_cpu(resp.req_resp) != VIRTIO_RPMB_RESP_PROGRAM_KEY) {
+			dev_err(dev, "Bad response from device (%x/%x)",
+				le16_to_cpu(resp.req_resp), le16_to_cpu(resp.result));
+			return -EPROTO;
+		}
+
+		/* check the MAC? */
+
+		/* map responses to better errors? */
+		return le16_to_cpu(resp.result) == VIRTIO_RPMB_RES_OK ? 0 : -EIO;
+	} else {
+		return -EINVAL;
+	}
+}
+
+static int rpmb_virtio_get_capacity(struct device *dev, u8 target)
+{
+	struct virtio_device *vdev = dev_to_virtio(dev);
+	u8 capacity;
+
+	virtio_cread(vdev, struct virtio_rpmb_config, capacity, &capacity);
+
+	if (capacity > 0x80) {
+		dev_err(&vdev->dev, "Error: invalid capacity reported.\n");
+		capacity = 0x80;
+	}
+
+	return capacity;
+}
+
+static int rpmb_virtio_get_write_count(struct device *dev, u8 target)
+{
+	struct virtio_device *vdev = dev_to_virtio(dev);
+	struct virtio_rpmb_info *vi = vdev->priv;
+	struct scatterlist out_frame;
+	struct scatterlist in_frame;
+	struct scatterlist *sgs[2];
+	unsigned int len;
+	struct virtio_rpmb_frame resp;
+	struct virtio_rpmb_frame cmd = {
+		.req_resp = cpu_to_le16(VIRTIO_RPMB_REQ_GET_WRITE_COUNTER)
+	};
+
+	mutex_lock(&vi->lock);
+
+	/* Wrap into SG array */
+	sg_init_one(&out_frame, &cmd, sizeof(cmd));
+	sg_init_one(&in_frame, &resp, sizeof(resp));
+	sgs[0] = &out_frame;
+	sgs[1] = &in_frame;
+
+	/* Send it */
+	virtqueue_add_sgs(vi->vq, sgs, 1, 1, vi, GFP_KERNEL);
+	virtqueue_kick(vi->vq);
+	wait_event(vi->have_data, virtqueue_get_buf(vi->vq, &len));
+
+	mutex_unlock(&vi->lock);
+
+	if (le16_to_cpu(resp.req_resp) != VIRTIO_RPMB_RESP_GET_COUNTER) {
+		dev_err(dev, "failed to program key (%x/%x)",
+			le16_to_cpu(resp.req_resp), le16_to_cpu(resp.result));
+		return -EPROTO;
+	}
+
+	return le16_to_cpu(resp.result) == VIRTIO_RPMB_RES_OK ? be32_to_cpu(resp.write_counter) : -EIO;
+}
+
+static struct rpmb_ops rpmb_virtio_ops = {
+	.program_key = rpmb_virtio_program_key,
+	.get_capacity = rpmb_virtio_get_capacity,
+	.get_write_count = rpmb_virtio_get_write_count,
+	/* .write_blocks = rpmb_virtio_write_blocks, */
+	/* .read_blocks = rpmb_virtio_read_blocks, */
+	.auth_method = RPMB_HMAC_ALGO_SHA_256,
+};
+
+static int rpmb_virtio_dev_init(struct virtio_rpmb_info *vi)
+{
+	int ret = 0;
+	struct device *dev = &vi->vq->vdev->dev;
+	struct virtio_device *vdev = dev_to_virtio(dev);
+	u8 max_wr, max_rd;
+
+	virtio_cread(vdev, struct virtio_rpmb_config,
+		     max_wr_cnt, &max_wr);
+	virtio_cread(vdev, struct virtio_rpmb_config,
+		     max_rd_cnt, &max_rd);
+
+	rpmb_virtio_ops.dev_id_len = strlen(id);
+	rpmb_virtio_ops.dev_id = id;
+	rpmb_virtio_ops.wr_cnt_max = max_wr;
+	rpmb_virtio_ops.rd_cnt_max = max_rd;
+	rpmb_virtio_ops.block_size = 1;
+
+	vi->rdev = rpmb_dev_register(dev, 0, &rpmb_virtio_ops);
+	if (IS_ERR(vi->rdev)) {
+		ret = PTR_ERR(vi->rdev);
+		goto err;
+	}
+
+	dev_set_drvdata(dev, vi);
+err:
+	return ret;
+}
+
+static int virtio_rpmb_init(struct virtio_device *vdev)
+{
+	int ret;
+	struct virtio_rpmb_info *vi;
+
+	vi = kzalloc(sizeof(*vi), GFP_KERNEL);
+	if (!vi)
+		return -ENOMEM;
+
+	init_waitqueue_head(&vi->have_data);
+	mutex_init(&vi->lock);
+	vdev->priv = vi;
+
+	/* We expect a single virtqueue. */
+	vi->vq = virtio_find_single_vq(vdev, virtio_rpmb_recv_done, "request");
+	if (IS_ERR(vi->vq)) {
+		dev_err(&vdev->dev, "get single vq failed!\n");
+		ret = PTR_ERR(vi->vq);
+		goto err;
+	}
+
+	/* create vrpmb device. */
+	ret = rpmb_virtio_dev_init(vi);
+	if (ret) {
+		dev_err(&vdev->dev, "create vrpmb device failed.\n");
+		goto err;
+	}
+
+	dev_info(&vdev->dev, "init done!\n");
+
+	return 0;
+
+err:
+	kfree(vi);
+	return ret;
+}
+
+static void virtio_rpmb_remove(struct virtio_device *vdev)
+{
+	struct virtio_rpmb_info *vi;
+
+	vi = vdev->priv;
+	if (!vi)
+		return;
+
+	if (wq_has_sleeper(&vi->have_data))
+		wake_up(&vi->have_data);
+
+	rpmb_dev_unregister(vi->rdev);
+
+	if (vdev->config->reset)
+		vdev->config->reset(vdev);
+
+	if (vdev->config->del_vqs)
+		vdev->config->del_vqs(vdev);
+
+	kfree(vi);
+}
+
+static int virtio_rpmb_probe(struct virtio_device *vdev)
+{
+	return virtio_rpmb_init(vdev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int virtio_rpmb_freeze(struct virtio_device *vdev)
+{
+	virtio_rpmb_remove(vdev);
+	return 0;
+}
+
+static int virtio_rpmb_restore(struct virtio_device *vdev)
+{
+	return virtio_rpmb_init(vdev);
+}
+#endif
+
+static struct virtio_device_id id_table[] = {
+	{ VIRTIO_ID_RPMB, VIRTIO_DEV_ANY_ID },
+	{ 0 },
+};
+
+static struct virtio_driver virtio_rpmb_driver = {
+	.driver.name =	KBUILD_MODNAME,
+	.driver.owner =	THIS_MODULE,
+	.id_table =	id_table,
+	.probe =	virtio_rpmb_probe,
+	.remove =	virtio_rpmb_remove,
+#ifdef CONFIG_PM_SLEEP
+	.freeze =	virtio_rpmb_freeze,
+	.restore =	virtio_rpmb_restore,
+#endif
+};
+
+module_virtio_driver(virtio_rpmb_driver);
+MODULE_DEVICE_TABLE(virtio, id_table);
+
+MODULE_DESCRIPTION("Virtio rpmb frontend driver");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
index bc1c0621f5ed..934df72714e2 100644
--- a/include/uapi/linux/virtio_ids.h
+++ b/include/uapi/linux/virtio_ids.h
@@ -53,6 +53,7 @@
 #define VIRTIO_ID_MEM			24 /* virtio mem */
 #define VIRTIO_ID_FS			26 /* virtio filesystem */
 #define VIRTIO_ID_PMEM			27 /* virtio pmem */
+#define VIRTIO_ID_RPMB			28 /* virtio RPMB */
 #define VIRTIO_ID_MAC80211_HWSIM	29 /* virtio mac80211-hwsim */
 
 #endif /* _LINUX_VIRTIO_IDS_H */
diff --git a/include/uapi/linux/virtio_rpmb.h b/include/uapi/linux/virtio_rpmb.h
new file mode 100644
index 000000000000..f048cd968210
--- /dev/null
+++ b/include/uapi/linux/virtio_rpmb.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+
+#ifndef _UAPI_LINUX_VIRTIO_RPMB_H
+#define _UAPI_LINUX_VIRTIO_RPMB_H
+
+#include <linux/types.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_types.h>
+
+struct virtio_rpmb_config {
+	__u8 capacity;
+	__u8 max_wr_cnt;
+	__u8 max_rd_cnt;
+} __attribute__((packed));
+
+/* RPMB Request Types (in .req_resp) */
+#define VIRTIO_RPMB_REQ_PROGRAM_KEY        0x0001
+#define VIRTIO_RPMB_REQ_GET_WRITE_COUNTER  0x0002
+#define VIRTIO_RPMB_REQ_DATA_WRITE         0x0003
+#define VIRTIO_RPMB_REQ_DATA_READ          0x0004
+#define VIRTIO_RPMB_REQ_RESULT_READ        0x0005
+
+/* RPMB Response Types (in .req_resp) */
+#define VIRTIO_RPMB_RESP_PROGRAM_KEY       0x0100
+#define VIRTIO_RPMB_RESP_GET_COUNTER       0x0200
+#define VIRTIO_RPMB_RESP_DATA_WRITE        0x0300
+#define VIRTIO_RPMB_RESP_DATA_READ         0x0400
+
+struct virtio_rpmb_frame {
+	__u8 stuff[196];
+	__u8 key_mac[32];
+	__u8 data[256];
+	__u8 nonce[16];
+	__be32 write_counter;
+	__be16 address;
+	__be16 block_count;
+	__be16 result;
+	__be16 req_resp;
+} __attribute__((packed));
+
+/* RPMB Operation Results (in .result) */
+#define VIRTIO_RPMB_RES_OK                     0x0000
+#define VIRTIO_RPMB_RES_GENERAL_FAILURE        0x0001
+#define VIRTIO_RPMB_RES_AUTH_FAILURE           0x0002
+#define VIRTIO_RPMB_RES_COUNT_FAILURE          0x0003
+#define VIRTIO_RPMB_RES_ADDR_FAILURE           0x0004
+#define VIRTIO_RPMB_RES_WRITE_FAILURE          0x0005
+#define VIRTIO_RPMB_RES_READ_FAILURE           0x0006
+#define VIRTIO_RPMB_RES_NO_AUTH_KEY            0x0007
+#define VIRTIO_RPMB_RES_WRITE_COUNTER_EXPIRED  0x0080
+
+
+#endif /* _UAPI_LINUX_VIRTIO_RPMB_H */
-- 
2.20.1


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

* [RFC PATCH  5/5] tools/rpmb: simple test sequence
  2021-03-03 13:54 [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend Alex Bennée
                   ` (3 preceding siblings ...)
  2021-03-03 13:54 ` [RFC PATCH 4/5] rpmb: create virtio rpmb frontend driver [WIP] Alex Bennée
@ 2021-03-03 13:55 ` Alex Bennée
  2021-03-09 13:27 ` [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend Avri Altman
  5 siblings, 0 replies; 47+ messages in thread
From: Alex Bennée @ 2021-03-03 13:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: maxim.uvarov, joakim.bech, ilias.apalodimas, arnd, ruchika.gupta,
	tomas.winkler, yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc,
	linux-scsi, linux-nvme, Alex Bennée

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 1060 bytes --]

A simple test script to exercise the rpmb interface.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 tools/rpmb/key     |  1 +
 tools/rpmb/test.sh | 13 +++++++++++++
 2 files changed, 14 insertions(+)
 create mode 100644 tools/rpmb/key
 create mode 100755 tools/rpmb/test.sh

diff --git a/tools/rpmb/key b/tools/rpmb/key
new file mode 100644
index 000000000000..2b6bd3bc3fe6
--- /dev/null
+++ b/tools/rpmb/key
@@ -0,0 +1 @@
+˜ƒÆÐh«#×¢ö‹pRTà¿®å\x1fô\x1f\r|OŠ	¯mo«\x10
\ No newline at end of file
diff --git a/tools/rpmb/test.sh b/tools/rpmb/test.sh
new file mode 100755
index 000000000000..ae5ce49a412f
--- /dev/null
+++ b/tools/rpmb/test.sh
@@ -0,0 +1,13 @@
+#!/bin/sh -e
+echo "get info"
+./rpmb -v get-info /dev/rpmb0
+echo "program key"
+./rpmb -v program-key /dev/rpmb0 key
+echo "get write counter"
+./rpmb -v write-counter /dev/rpmb0
+echo "generating data"
+dd if=/dev/urandom of=data.in count=1 bs=256
+echo "write data"
+./rpmb -v write-blocks /dev/rpmb0 0 1 data.in
+echo "read data back"
+./rpmb -v read-blocks /dev/rpmb0 0 1 data.out
-- 
2.20.1


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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-03 13:54 ` [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem Alex Bennée
@ 2021-03-03 15:28   ` Ulf Hansson
  2021-03-03 19:37     ` Alex Bennée
  2021-03-04 20:56   ` Arnd Bergmann
  1 sibling, 1 reply; 47+ messages in thread
From: Ulf Hansson @ 2021-03-03 15:28 UTC (permalink / raw)
  To: Alex Bennée
  Cc: Linux Kernel Mailing List, Maxim Uvarov, Joakim Bech,
	Ilias Apalodimas, Arnd Bergmann, Ruchika Gupta, Tomas Winkler,
	yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc, linux-scsi,
	linux-nvme, Linus Walleij, Arnd Bergmann

On Wed, 3 Mar 2021 at 14:55, Alex Bennée <alex.bennee@linaro.org> wrote:
>
> A number of storage technologies support a specialised hardware
> partition designed to be resistant to replay attacks. The underlying
> HW protocols differ but the operations are common. The RPMB partition
> cannot be accessed via standard block layer, but by a set of specific
> commands: WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY. Such a
> partition provides authenticated and replay protected access, hence
> suitable as a secure storage.
>
> The RPMB layer aims to provide in-kernel API for Trusted Execution
> Environment (TEE) devices that are capable to securely compute block
> frame signature. In case a TEE device wishes to store a replay
> protected data, requests the storage device via RPMB layer to store
> the data.
>
> A TEE device driver can claim the RPMB interface, for example, via
> class_interface_register(). The RPMB layer provides a series of
> operations for interacting with the device.
>
>   * program_key - a one time operation for setting up a new device
>   * get_capacity - introspect the device capacity
>   * get_write_count - check the write counter
>   * write_blocks - write a series of blocks to the RPMB device
>   * read_blocks - read a series of blocks from the RPMB device
>
> The detailed operation of implementing the access is left to the TEE
> device driver itself.
>
> [This is based-on Thomas Winkler's proposed API from:
>
>   https://lore.kernel.org/linux-mmc/1478548394-8184-2-git-send-email-tomas.winkler@intel.com/
>
> The principle difference is the framing details and HW specific
> bits (JDEC vs NVME frames) are left to the lower level TEE driver to
> worry about. The eventual userspace ioctl interface will aim to be
> similarly generic. This is an RFC to follow up on:
>
>   Subject: RPMB user space ABI
>   Date: Thu, 11 Feb 2021 14:07:00 +0000
>   Message-ID: <87mtwashi4.fsf@linaro.org>]
>
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> Cc: Tomas Winkler <tomas.winkler@intel.com>
> Cc: Ulf Hansson <ulf.hansson@linaro.org>
> Cc: Linus  Walleij <linus.walleij@linaro.org>
> Cc: Arnd Bergmann <arnd.bergmann@linaro.org>
> Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>

Alex, I promise to have a closer look at this and provide my opinions.

However, it looks like you have posted patch 1 and patch2, but the
remainder 3, 4, 5 I can't find. Was this perhaps intentional?

Moreover, I think these kinds of changes deserve a proper
cover-letter, describing the overall goal with the series. Can you
perhaps re-submit, so clarify things.

Kind regards
Uffe

> ---
>  MAINTAINERS                |   7 +
>  drivers/char/Kconfig       |   2 +
>  drivers/char/Makefile      |   1 +
>  drivers/char/rpmb/Kconfig  |  11 +
>  drivers/char/rpmb/Makefile |   7 +
>  drivers/char/rpmb/core.c   | 429 +++++++++++++++++++++++++++++++++++++
>  include/linux/rpmb.h       | 163 ++++++++++++++
>  7 files changed, 620 insertions(+)
>  create mode 100644 drivers/char/rpmb/Kconfig
>  create mode 100644 drivers/char/rpmb/Makefile
>  create mode 100644 drivers/char/rpmb/core.c
>  create mode 100644 include/linux/rpmb.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index bfc1b86e3e73..076f3983526c 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15369,6 +15369,13 @@ T:     git git://linuxtv.org/media_tree.git
>  F:     Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml
>  F:     drivers/media/platform/sunxi/sun8i-rotate/
>
> +RPMB SUBSYSTEM
> +M:     ?
> +L:     linux-kernel@vger.kernel.org
> +S:     Supported
> +F:     drivers/char/rpmb/*
> +F:     include/linux/rpmb.h
> +
>  RTL2830 MEDIA DRIVER
>  M:     Antti Palosaari <crope@iki.fi>
>  L:     linux-media@vger.kernel.org
> diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
> index d229a2d0c017..a7834cc3e0ea 100644
> --- a/drivers/char/Kconfig
> +++ b/drivers/char/Kconfig
> @@ -471,6 +471,8 @@ config ADI
>           and SSM (Silicon Secured Memory).  Intended consumers of this
>           driver include crash and makedumpfile.
>
> +source "drivers/char/rpmb/Kconfig"
> +
>  endmenu
>
>  config RANDOM_TRUST_CPU
> diff --git a/drivers/char/Makefile b/drivers/char/Makefile
> index ffce287ef415..0eed6e21a7a7 100644
> --- a/drivers/char/Makefile
> +++ b/drivers/char/Makefile
> @@ -47,3 +47,4 @@ obj-$(CONFIG_PS3_FLASH)               += ps3flash.o
>  obj-$(CONFIG_XILLYBUS)         += xillybus/
>  obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o
>  obj-$(CONFIG_ADI)              += adi.o
> +obj-$(CONFIG_RPMB)             += rpmb/
> diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig
> new file mode 100644
> index 000000000000..431c2823cf70
> --- /dev/null
> +++ b/drivers/char/rpmb/Kconfig
> @@ -0,0 +1,11 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2015-2019, Intel Corporation.
> +
> +config RPMB
> +       tristate "RPMB partition interface"
> +       help
> +         Unified RPMB partition interface for eMMC and UFS.
> +         Provides interface for in kernel security controllers to
> +         access RPMB partition.
> +
> +         If unsure, select N.
> diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile
> new file mode 100644
> index 000000000000..24d4752a9a53
> --- /dev/null
> +++ b/drivers/char/rpmb/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2015-2019, Intel Corporation.
> +
> +obj-$(CONFIG_RPMB) += rpmb.o
> +rpmb-objs += core.o
> +
> +ccflags-y += -D__CHECK_ENDIAN__
> diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c
> new file mode 100644
> index 000000000000..a2e21c14986a
> --- /dev/null
> +++ b/drivers/char/rpmb/core.c
> @@ -0,0 +1,429 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright(c) 2015 - 2019 Intel Corporation. All rights reserved.
> + * Copyright(c) 2021 - Linaro Ltd.
> + */
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/mutex.h>
> +#include <linux/list.h>
> +#include <linux/device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/rpmb.h>
> +
> +static DEFINE_IDA(rpmb_ida);
> +
> +/**
> + * rpmb_dev_get() - increase rpmb device ref counter
> + * @rdev: rpmb device
> + */
> +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
> +{
> +       return get_device(&rdev->dev) ? rdev : NULL;
> +}
> +EXPORT_SYMBOL_GPL(rpmb_dev_get);
> +
> +/**
> + * rpmb_dev_put() - decrease rpmb device ref counter
> + * @rdev: rpmb device
> + */
> +void rpmb_dev_put(struct rpmb_dev *rdev)
> +{
> +       put_device(&rdev->dev);
> +}
> +EXPORT_SYMBOL_GPL(rpmb_dev_put);
> +
> +/**
> + * rpmb_program_key() - program the RPMB access key
> + * @rdev: rpmb device
> + * @key: key data
> + * @keylen: length of key data
> + *
> + * A successful programming of the key implies it has been set by the
> + * driver and can be used.
> + *
> + * Return:
> + * *        0 on success
> + * *        -EINVAL on wrong parameters
> + * *        -EPERM key already programmed
> + * *        -EOPNOTSUPP if device doesn't support the requested operation
> + * *        < 0 if the operation fails
> + */
> +int rpmb_program_key(struct rpmb_dev *rdev, key_serial_t keyid)
> +{
> +       int err;
> +
> +       if (!rdev || !keyid)
> +               return -EINVAL;
> +
> +       mutex_lock(&rdev->lock);
> +       err = -EOPNOTSUPP;
> +       if (rdev->ops && rdev->ops->program_key) {
> +               err = rdev->ops->program_key(rdev->dev.parent, rdev->target,
> +                                            keyid);
> +       }
> +       mutex_unlock(&rdev->lock);
> +
> +       return err;
> +}
> +EXPORT_SYMBOL_GPL(rpmb_program_key);
> +
> +/**
> + * rpmb_get_capacity() - returns the capacity of the rpmb device
> + * @rdev: rpmb device
> + *
> + * Return:
> + * *        capacity of the device in units of 128K, on success
> + * *        -EINVAL on wrong parameters
> + * *        -EOPNOTSUPP if device doesn't support the requested operation
> + * *        < 0 if the operation fails
> + */
> +int rpmb_get_capacity(struct rpmb_dev *rdev)
> +{
> +       int err;
> +
> +       if (!rdev)
> +               return -EINVAL;
> +
> +       mutex_lock(&rdev->lock);
> +       err = -EOPNOTSUPP;
> +       if (rdev->ops && rdev->ops->get_capacity)
> +               err = rdev->ops->get_capacity(rdev->dev.parent, rdev->target);
> +       mutex_unlock(&rdev->lock);
> +
> +       return err;
> +}
> +EXPORT_SYMBOL_GPL(rpmb_get_capacity);
> +
> +/**
> + * rpmb_get_write_count() - returns the write counter of the rpmb device
> + * @rdev: rpmb device
> + *
> + * Return:
> + * *        counter
> + * *        -EINVAL on wrong parameters
> + * *        -EOPNOTSUPP if device doesn't support the requested operation
> + * *        < 0 if the operation fails
> + */
> +int rpmb_get_write_count(struct rpmb_dev *rdev)
> +{
> +       int err;
> +
> +       if (!rdev)
> +               return -EINVAL;
> +
> +       mutex_lock(&rdev->lock);
> +       err = -EOPNOTSUPP;
> +       if (rdev->ops && rdev->ops->get_write_count)
> +               err = rdev->ops->get_write_count(rdev->dev.parent, rdev->target);
> +       mutex_unlock(&rdev->lock);
> +
> +       return err;
> +}
> +EXPORT_SYMBOL_GPL(rpmb_get_write_count);
> +
> +/**
> + * rpmb_write_blocks() - write data to RPMB device
> + * @rdev: rpmb device
> + * @addr: block address (index of first block - 256B blocks)
> + * @count: number of 256B blosks
> + * @data: pointer to data to program
> + *
> + * Write a series of blocks to the RPMB device.
> + *
> + * Return:
> + * *        0 on success
> + * *        -EINVAL on wrong parameters
> + * *        -EACCESS no key set
> + * *        -EOPNOTSUPP if device doesn't support the requested operation
> + * *        < 0 if the operation fails
> + */
> +int rpmb_write_blocks(struct rpmb_dev *rdev, key_serial_t keyid, int addr,
> +                     int count, u8 *data)
> +{
> +       int err;
> +
> +       if (!rdev || !count || !data)
> +               return -EINVAL;
> +
> +       mutex_lock(&rdev->lock);
> +       err = -EOPNOTSUPP;
> +       if (rdev->ops && rdev->ops->write_blocks) {
> +               err = rdev->ops->write_blocks(rdev->dev.parent, rdev->target, keyid,
> +                                             addr, count, data);
> +       }
> +       mutex_unlock(&rdev->lock);
> +
> +       return err;
> +}
> +EXPORT_SYMBOL_GPL(rpmb_write_blocks);
> +
> +/**
> + * rpmb_read_blocks() - read data from RPMB device
> + * @rdev: rpmb device
> + * @addr: block address (index of first block - 256B blocks)
> + * @count: number of 256B blocks
> + * @data: pointer to data to read
> + *
> + * Read a series of one or more blocks from the RPMB device.
> + *
> + * Return:
> + * *        0 on success
> + * *        -EINVAL on wrong parameters
> + * *        -EACCESS no key set
> + * *        -EOPNOTSUPP if device doesn't support the requested operation
> + * *        < 0 if the operation fails
> + */
> +int rpmb_read_blocks(struct rpmb_dev *rdev, int addr, int count, u8 *data)
> +{
> +       int err;
> +
> +       if (!rdev || !count || !data)
> +               return -EINVAL;
> +
> +       mutex_lock(&rdev->lock);
> +       err = -EOPNOTSUPP;
> +       if (rdev->ops && rdev->ops->read_blocks) {
> +               err = rdev->ops->read_blocks(rdev->dev.parent, rdev->target,
> +                                            addr, count, data);
> +       }
> +       mutex_unlock(&rdev->lock);
> +
> +       return err;
> +}
> +EXPORT_SYMBOL_GPL(rpmb_read_blocks);
> +
> +
> +static void rpmb_dev_release(struct device *dev)
> +{
> +       struct rpmb_dev *rdev = to_rpmb_dev(dev);
> +
> +       ida_simple_remove(&rpmb_ida, rdev->id);
> +       kfree(rdev);
> +}
> +
> +struct class rpmb_class = {
> +       .name = "rpmb",
> +       .owner = THIS_MODULE,
> +       .dev_release = rpmb_dev_release,
> +};
> +EXPORT_SYMBOL(rpmb_class);
> +
> +/**
> + * rpmb_dev_find_device() - return first matching rpmb device
> + * @data: data for the match function
> + * @match: the matching function
> + *
> + * Return: matching rpmb device or NULL on failure
> + */
> +static
> +struct rpmb_dev *rpmb_dev_find_device(const void *data,
> +                                     int (*match)(struct device *dev,
> +                                                  const void *data))
> +{
> +       struct device *dev;
> +
> +       dev = class_find_device(&rpmb_class, NULL, data, match);
> +
> +       return dev ? to_rpmb_dev(dev) : NULL;
> +}
> +
> +struct device_with_target {
> +       const struct device *dev;
> +       u8 target;
> +};
> +
> +static int match_by_parent(struct device *dev, const void *data)
> +{
> +       const struct device_with_target *d = data;
> +       struct rpmb_dev *rdev = to_rpmb_dev(dev);
> +
> +       return (d->dev && dev->parent == d->dev && rdev->target == d->target);
> +}
> +
> +/**
> + * rpmb_dev_find_by_device() - retrieve rpmb device from the parent device
> + * @parent: parent device of the rpmb device
> + * @target: RPMB target/region within the physical device
> + *
> + * Return: NULL if there is no rpmb device associated with the parent device
> + */
> +struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target)
> +{
> +       struct device_with_target t;
> +
> +       if (!parent)
> +               return NULL;
> +
> +       t.dev = parent;
> +       t.target = target;
> +
> +       return rpmb_dev_find_device(&t, match_by_parent);
> +}
> +EXPORT_SYMBOL_GPL(rpmb_dev_find_by_device);
> +
> +/**
> + * rpmb_dev_unregister() - unregister RPMB partition from the RPMB subsystem
> + * @rdev: the rpmb device to unregister
> + * Return:
> + * *        0 on success
> + * *        -EINVAL on wrong parameters
> + */
> +int rpmb_dev_unregister(struct rpmb_dev *rdev)
> +{
> +       if (!rdev)
> +               return -EINVAL;
> +
> +       mutex_lock(&rdev->lock);
> +       device_del(&rdev->dev);
> +       mutex_unlock(&rdev->lock);
> +
> +       rpmb_dev_put(rdev);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(rpmb_dev_unregister);
> +
> +/**
> + * rpmb_dev_unregister_by_device() - unregister RPMB partition
> + *     from the RPMB subsystem
> + * @dev: the parent device of the rpmb device
> + * @target: RPMB target/region within the physical device
> + * Return:
> + * *        0 on success
> + * *        -EINVAL on wrong parameters
> + * *        -ENODEV if a device cannot be find.
> + */
> +int rpmb_dev_unregister_by_device(struct device *dev, u8 target)
> +{
> +       struct rpmb_dev *rdev;
> +
> +       if (!dev)
> +               return -EINVAL;
> +
> +       rdev = rpmb_dev_find_by_device(dev, target);
> +       if (!rdev) {
> +               dev_warn(dev, "no disk found %s\n", dev_name(dev->parent));
> +               return -ENODEV;
> +       }
> +
> +       rpmb_dev_put(rdev);
> +
> +       return rpmb_dev_unregister(rdev);
> +}
> +EXPORT_SYMBOL_GPL(rpmb_dev_unregister_by_device);
> +
> +/**
> + * rpmb_dev_get_drvdata() - driver data getter
> + * @rdev: rpmb device
> + *
> + * Return: driver private data
> + */
> +void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev)
> +{
> +       return dev_get_drvdata(&rdev->dev);
> +}
> +EXPORT_SYMBOL_GPL(rpmb_dev_get_drvdata);
> +
> +/**
> + * rpmb_dev_set_drvdata() - driver data setter
> + * @rdev: rpmb device
> + * @data: data to store
> + */
> +void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data)
> +{
> +       dev_set_drvdata(&rdev->dev, data);
> +}
> +EXPORT_SYMBOL_GPL(rpmb_dev_set_drvdata);
> +
> +/**
> + * rpmb_dev_register - register RPMB partition with the RPMB subsystem
> + * @dev: storage device of the rpmb device
> + * @target: RPMB target/region within the physical device
> + * @ops: device specific operations
> + *
> + * Return: a pointer to rpmb device
> + */
> +struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target,
> +                                  const struct rpmb_ops *ops)
> +{
> +       struct rpmb_dev *rdev;
> +       int id;
> +       int ret;
> +
> +       if (!dev || !ops)
> +               return ERR_PTR(-EINVAL);
> +
> +       if (!ops->program_key)
> +               return ERR_PTR(-EINVAL);
> +
> +       if (!ops->get_capacity)
> +               return ERR_PTR(-EINVAL);
> +
> +       if (!ops->get_write_count)
> +               return ERR_PTR(-EINVAL);
> +
> +       if (!ops->write_blocks)
> +               return ERR_PTR(-EINVAL);
> +
> +       if (!ops->read_blocks)
> +               return ERR_PTR(-EINVAL);
> +
> +       if (ops->type == RPMB_TYPE_ANY || ops->type > RPMB_TYPE_MAX)
> +               return ERR_PTR(-EINVAL);
> +
> +       rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
> +       if (!rdev)
> +               return ERR_PTR(-ENOMEM);
> +
> +       id = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL);
> +       if (id < 0) {
> +               ret = id;
> +               goto exit;
> +       }
> +
> +       mutex_init(&rdev->lock);
> +       rdev->ops = ops;
> +       rdev->id = id;
> +       rdev->target = target;
> +
> +       dev_set_name(&rdev->dev, "rpmb%d", id);
> +       rdev->dev.class = &rpmb_class;
> +       rdev->dev.parent = dev;
> +       ret = device_register(&rdev->dev);
> +       if (ret)
> +               goto exit;
> +
> +       dev_dbg(&rdev->dev, "registered device\n");
> +
> +       return rdev;
> +
> +exit:
> +       if (id >= 0)
> +               ida_simple_remove(&rpmb_ida, id);
> +       kfree(rdev);
> +       return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(rpmb_dev_register);
> +
> +static int __init rpmb_init(void)
> +{
> +       ida_init(&rpmb_ida);
> +       class_register(&rpmb_class);
> +       return 0;
> +}
> +
> +static void __exit rpmb_exit(void)
> +{
> +       class_unregister(&rpmb_class);
> +       ida_destroy(&rpmb_ida);
> +}
> +
> +subsys_initcall(rpmb_init);
> +module_exit(rpmb_exit);
> +
> +MODULE_AUTHOR("Intel Corporation");
> +MODULE_DESCRIPTION("RPMB class");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h
> new file mode 100644
> index 000000000000..718ba7c91ecd
> --- /dev/null
> +++ b/include/linux/rpmb.h
> @@ -0,0 +1,163 @@
> +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
> +/*
> + * Copyright (C) 2015-2019 Intel Corp. All rights reserved
> + * Copyright (C) 2021 Linaro Ltd
> + */
> +#ifndef __RPMB_H__
> +#define __RPMB_H__
> +
> +#include <linux/types.h>
> +#include <linux/device.h>
> +#include <linux/kref.h>
> +#include <linux/key.h>
> +
> +/**
> + * struct rpmb_ops - RPMB ops to be implemented by underlying block device
> + *
> + * @program_key    : program device key (once only op).
> + * @get_capacity   : rpmb size in 128K units in for region/target.
> + * @get_write_count: return the device write counter
> + * @write_blocks   : write blocks to RPMB device
> + * @read_blocks    : read blocks from RPMB device
> + * @block_size     : block size in half sectors (1 == 256B)
> + * @wr_cnt_max     : maximal number of blocks that can be
> + *                   written in one access.
> + * @rd_cnt_max     : maximal number of blocks that can be
> + *                   read in one access.
> + * @auth_method    : rpmb_auth_method
> + * @dev_id         : unique device identifier
> + * @dev_id_len     : unique device identifier length
> + */
> +struct rpmb_ops {
> +       int (*program_key)(struct device *dev, u8 target, key_serial_t keyid);
> +       int (*get_capacity)(struct device *dev, u8 target);
> +       int (*get_write_count)(struct device *dev, u8 target);
> +       int (*write_blocks)(struct device *dev, u8 target, key_serial_t keyid,
> +                           int addr, int count, u8 *data);
> +       int (*read_blocks)(struct device *dev, u8 target,
> +                          int addr, int count, u8 *data);
> +       u16 block_size;
> +       u16 wr_cnt_max;
> +       u16 rd_cnt_max;
> +       u16 auth_method;
> +       const u8 *dev_id;
> +       size_t dev_id_len;
> +};
> +
> +/**
> + * struct rpmb_dev - device which can support RPMB partition
> + *
> + * @lock       : the device lock
> + * @dev        : device
> + * @id         : device id
> + * @target     : RPMB target/region within the physical device
> + * @ops        : operation exported by block layer
> + */
> +struct rpmb_dev {
> +       struct mutex lock; /* device serialization lock */
> +       struct device dev;
> +       int id;
> +       u8 target;
> +       const struct rpmb_ops *ops;
> +};
> +
> +#define to_rpmb_dev(x) container_of((x), struct rpmb_dev, dev)
> +
> +#if IS_ENABLED(CONFIG_RPMB)
> +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev);
> +void rpmb_dev_put(struct rpmb_dev *rdev);
> +struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target);
> +struct rpmb_dev *rpmb_dev_get_by_type(u32 type);
> +struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target,
> +                                  const struct rpmb_ops *ops);
> +void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev);
> +void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data);
> +int rpmb_dev_unregister(struct rpmb_dev *rdev);
> +int rpmb_dev_unregister_by_device(struct device *dev, u8 target);
> +int rpmb_program_key(struct rpmb_dev *rdev, key_serial_t keyid);
> +int rpmb_get_capacity(struct rpmb_dev *rdev);
> +int rpmb_get_write_count(struct rpmb_dev *rdev);
> +int rpmb_write_blocks(struct rpmb_dev *rdev, key_serial_t keyid,
> +                     int addr, int count, u8 *data);
> +int rpmb_read_blocks(struct rpmb_dev *rdev, int addr, int count, u8 *data);
> +
> +#else
> +static inline struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
> +{
> +       return NULL;
> +}
> +
> +static inline void rpmb_dev_put(struct rpmb_dev *rdev) { }
> +
> +static inline struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent,
> +                                                      u8 target)
> +{
> +       return NULL;
> +}
> +
> +static inline
> +struct rpmb_dev *rpmb_dev_get_by_type(enum rpmb_type type)
> +{
> +       return NULL;
> +}
> +
> +static inline void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev)
> +{
> +       return NULL;
> +}
> +
> +static inline void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data)
> +{
> +}
> +
> +static inline struct rpmb_dev *
> +rpmb_dev_register(struct device *dev, u8 target, const struct rpmb_ops *ops)
> +{
> +       return NULL;
> +}
> +
> +static inline int rpmb_dev_unregister(struct rpmb_dev *dev)
> +{
> +       return 0;
> +}
> +
> +static inline int rpmb_dev_unregister_by_device(struct device *dev, u8 target)
> +{
> +       return 0;
> +}
> +
> +static inline int rpmb_program_key(struct rpmb_dev *rdev, key_serial_t keyid)
> +{
> +       return 0;
> +}
> +
> +static inline rpmb_set_key(struct rpmb_dev *rdev, u8 *key, int keylen);
> +{
> +       return 0;
> +}
> +
> +static inline int rpmb_get_capacity(struct rpmb_dev *rdev)
> +{
> +       return 0;
> +}
> +
> +static inline int rpmb_get_write_count(struct rpmb_dev *rdev)
> +{
> +       return 0;
> +}
> +
> +static inline int rpmb_write_blocks(struct rpmb_dev *rdev, int addr, int count,
> +                                   u8 *data)
> +{
> +       return 0;
> +}
> +
> +static inline int rpmb_read_blocks(struct rpmb_dev *rdev, int addr, int count,
> +                                  u8 *data)
> +{
> +       return 0;
> +}
> +
> +#endif /* CONFIG_RPMB */
> +
> +#endif /* __RPMB_H__ */
> --
> 2.20.1
>

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-03 15:28   ` Ulf Hansson
@ 2021-03-03 19:37     ` Alex Bennée
  0 siblings, 0 replies; 47+ messages in thread
From: Alex Bennée @ 2021-03-03 19:37 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Linux Kernel Mailing List, Maxim Uvarov, Joakim Bech,
	Ilias Apalodimas, Arnd Bergmann, Ruchika Gupta, Tomas Winkler,
	Yang Huang, Bing Zhu, Matti Möll, Harald Mommer, linux-mmc,
	linux-scsi, linux-nvme, Linus Walleij, Arnd Bergmann

Foolishly I'd missed you out of the series Cc so you only got those
two patches. You should find the rest @

Subject: [RFC PATCH  0/5] RPMB internal and user-space API + WIP
virtio-rpmb frontend
Date: Wed,  3 Mar 2021 13:54:55 +0000
Message-Id: <20210303135500.24673-1-alex.bennee@linaro.org>

assuming you are subscribed to one of the Cc'd lists.

On Wed, 3 Mar 2021 at 15:29, Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> On Wed, 3 Mar 2021 at 14:55, Alex Bennée <alex.bennee@linaro.org> wrote:
> >
> > A number of storage technologies support a specialised hardware
> > partition designed to be resistant to replay attacks. The underlying
> > HW protocols differ but the operations are common. The RPMB partition
> > cannot be accessed via standard block layer, but by a set of specific
> > commands: WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY. Such a
> > partition provides authenticated and replay protected access, hence
> > suitable as a secure storage.
> >
> > The RPMB layer aims to provide in-kernel API for Trusted Execution
> > Environment (TEE) devices that are capable to securely compute block
> > frame signature. In case a TEE device wishes to store a replay
> > protected data, requests the storage device via RPMB layer to store
> > the data.
> >
> > A TEE device driver can claim the RPMB interface, for example, via
> > class_interface_register(). The RPMB layer provides a series of
> > operations for interacting with the device.
> >
> >   * program_key - a one time operation for setting up a new device
> >   * get_capacity - introspect the device capacity
> >   * get_write_count - check the write counter
> >   * write_blocks - write a series of blocks to the RPMB device
> >   * read_blocks - read a series of blocks from the RPMB device
> >
> > The detailed operation of implementing the access is left to the TEE
> > device driver itself.
> >
> > [This is based-on Thomas Winkler's proposed API from:
> >
> >   https://lore.kernel.org/linux-mmc/1478548394-8184-2-git-send-email-tomas.winkler@intel.com/
> >
> > The principle difference is the framing details and HW specific
> > bits (JDEC vs NVME frames) are left to the lower level TEE driver to
> > worry about. The eventual userspace ioctl interface will aim to be
> > similarly generic. This is an RFC to follow up on:
> >
> >   Subject: RPMB user space ABI
> >   Date: Thu, 11 Feb 2021 14:07:00 +0000
> >   Message-ID: <87mtwashi4.fsf@linaro.org>]
> >
> > Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> > Cc: Tomas Winkler <tomas.winkler@intel.com>
> > Cc: Ulf Hansson <ulf.hansson@linaro.org>
> > Cc: Linus  Walleij <linus.walleij@linaro.org>
> > Cc: Arnd Bergmann <arnd.bergmann@linaro.org>
> > Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
>
> Alex, I promise to have a closer look at this and provide my opinions.
>
> However, it looks like you have posted patch 1 and patch2, but the
> remainder 3, 4, 5 I can't find. Was this perhaps intentional?
>
> Moreover, I think these kinds of changes deserve a proper
> cover-letter, describing the overall goal with the series. Can you
> perhaps re-submit, so clarify things.
>
> Kind regards
> Uffe
>
> > ---
> >  MAINTAINERS                |   7 +
> >  drivers/char/Kconfig       |   2 +
> >  drivers/char/Makefile      |   1 +
> >  drivers/char/rpmb/Kconfig  |  11 +
> >  drivers/char/rpmb/Makefile |   7 +
> >  drivers/char/rpmb/core.c   | 429 +++++++++++++++++++++++++++++++++++++
> >  include/linux/rpmb.h       | 163 ++++++++++++++
> >  7 files changed, 620 insertions(+)
> >  create mode 100644 drivers/char/rpmb/Kconfig
> >  create mode 100644 drivers/char/rpmb/Makefile
> >  create mode 100644 drivers/char/rpmb/core.c
> >  create mode 100644 include/linux/rpmb.h
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index bfc1b86e3e73..076f3983526c 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -15369,6 +15369,13 @@ T:     git git://linuxtv.org/media_tree.git
> >  F:     Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml
> >  F:     drivers/media/platform/sunxi/sun8i-rotate/
> >
> > +RPMB SUBSYSTEM
> > +M:     ?
> > +L:     linux-kernel@vger.kernel.org
> > +S:     Supported
> > +F:     drivers/char/rpmb/*
> > +F:     include/linux/rpmb.h
> > +
> >  RTL2830 MEDIA DRIVER
> >  M:     Antti Palosaari <crope@iki.fi>
> >  L:     linux-media@vger.kernel.org
> > diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
> > index d229a2d0c017..a7834cc3e0ea 100644
> > --- a/drivers/char/Kconfig
> > +++ b/drivers/char/Kconfig
> > @@ -471,6 +471,8 @@ config ADI
> >           and SSM (Silicon Secured Memory).  Intended consumers of this
> >           driver include crash and makedumpfile.
> >
> > +source "drivers/char/rpmb/Kconfig"
> > +
> >  endmenu
> >
> >  config RANDOM_TRUST_CPU
> > diff --git a/drivers/char/Makefile b/drivers/char/Makefile
> > index ffce287ef415..0eed6e21a7a7 100644
> > --- a/drivers/char/Makefile
> > +++ b/drivers/char/Makefile
> > @@ -47,3 +47,4 @@ obj-$(CONFIG_PS3_FLASH)               += ps3flash.o
> >  obj-$(CONFIG_XILLYBUS)         += xillybus/
> >  obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o
> >  obj-$(CONFIG_ADI)              += adi.o
> > +obj-$(CONFIG_RPMB)             += rpmb/
> > diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig
> > new file mode 100644
> > index 000000000000..431c2823cf70
> > --- /dev/null
> > +++ b/drivers/char/rpmb/Kconfig
> > @@ -0,0 +1,11 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +# Copyright (c) 2015-2019, Intel Corporation.
> > +
> > +config RPMB
> > +       tristate "RPMB partition interface"
> > +       help
> > +         Unified RPMB partition interface for eMMC and UFS.
> > +         Provides interface for in kernel security controllers to
> > +         access RPMB partition.
> > +
> > +         If unsure, select N.
> > diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile
> > new file mode 100644
> > index 000000000000..24d4752a9a53
> > --- /dev/null
> > +++ b/drivers/char/rpmb/Makefile
> > @@ -0,0 +1,7 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +# Copyright (c) 2015-2019, Intel Corporation.
> > +
> > +obj-$(CONFIG_RPMB) += rpmb.o
> > +rpmb-objs += core.o
> > +
> > +ccflags-y += -D__CHECK_ENDIAN__
> > diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c
> > new file mode 100644
> > index 000000000000..a2e21c14986a
> > --- /dev/null
> > +++ b/drivers/char/rpmb/core.c
> > @@ -0,0 +1,429 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright(c) 2015 - 2019 Intel Corporation. All rights reserved.
> > + * Copyright(c) 2021 - Linaro Ltd.
> > + */
> > +#include <linux/module.h>
> > +#include <linux/init.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mutex.h>
> > +#include <linux/list.h>
> > +#include <linux/device.h>
> > +#include <linux/slab.h>
> > +
> > +#include <linux/rpmb.h>
> > +
> > +static DEFINE_IDA(rpmb_ida);
> > +
> > +/**
> > + * rpmb_dev_get() - increase rpmb device ref counter
> > + * @rdev: rpmb device
> > + */
> > +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
> > +{
> > +       return get_device(&rdev->dev) ? rdev : NULL;
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_dev_get);
> > +
> > +/**
> > + * rpmb_dev_put() - decrease rpmb device ref counter
> > + * @rdev: rpmb device
> > + */
> > +void rpmb_dev_put(struct rpmb_dev *rdev)
> > +{
> > +       put_device(&rdev->dev);
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_dev_put);
> > +
> > +/**
> > + * rpmb_program_key() - program the RPMB access key
> > + * @rdev: rpmb device
> > + * @key: key data
> > + * @keylen: length of key data
> > + *
> > + * A successful programming of the key implies it has been set by the
> > + * driver and can be used.
> > + *
> > + * Return:
> > + * *        0 on success
> > + * *        -EINVAL on wrong parameters
> > + * *        -EPERM key already programmed
> > + * *        -EOPNOTSUPP if device doesn't support the requested operation
> > + * *        < 0 if the operation fails
> > + */
> > +int rpmb_program_key(struct rpmb_dev *rdev, key_serial_t keyid)
> > +{
> > +       int err;
> > +
> > +       if (!rdev || !keyid)
> > +               return -EINVAL;
> > +
> > +       mutex_lock(&rdev->lock);
> > +       err = -EOPNOTSUPP;
> > +       if (rdev->ops && rdev->ops->program_key) {
> > +               err = rdev->ops->program_key(rdev->dev.parent, rdev->target,
> > +                                            keyid);
> > +       }
> > +       mutex_unlock(&rdev->lock);
> > +
> > +       return err;
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_program_key);
> > +
> > +/**
> > + * rpmb_get_capacity() - returns the capacity of the rpmb device
> > + * @rdev: rpmb device
> > + *
> > + * Return:
> > + * *        capacity of the device in units of 128K, on success
> > + * *        -EINVAL on wrong parameters
> > + * *        -EOPNOTSUPP if device doesn't support the requested operation
> > + * *        < 0 if the operation fails
> > + */
> > +int rpmb_get_capacity(struct rpmb_dev *rdev)
> > +{
> > +       int err;
> > +
> > +       if (!rdev)
> > +               return -EINVAL;
> > +
> > +       mutex_lock(&rdev->lock);
> > +       err = -EOPNOTSUPP;
> > +       if (rdev->ops && rdev->ops->get_capacity)
> > +               err = rdev->ops->get_capacity(rdev->dev.parent, rdev->target);
> > +       mutex_unlock(&rdev->lock);
> > +
> > +       return err;
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_get_capacity);
> > +
> > +/**
> > + * rpmb_get_write_count() - returns the write counter of the rpmb device
> > + * @rdev: rpmb device
> > + *
> > + * Return:
> > + * *        counter
> > + * *        -EINVAL on wrong parameters
> > + * *        -EOPNOTSUPP if device doesn't support the requested operation
> > + * *        < 0 if the operation fails
> > + */
> > +int rpmb_get_write_count(struct rpmb_dev *rdev)
> > +{
> > +       int err;
> > +
> > +       if (!rdev)
> > +               return -EINVAL;
> > +
> > +       mutex_lock(&rdev->lock);
> > +       err = -EOPNOTSUPP;
> > +       if (rdev->ops && rdev->ops->get_write_count)
> > +               err = rdev->ops->get_write_count(rdev->dev.parent, rdev->target);
> > +       mutex_unlock(&rdev->lock);
> > +
> > +       return err;
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_get_write_count);
> > +
> > +/**
> > + * rpmb_write_blocks() - write data to RPMB device
> > + * @rdev: rpmb device
> > + * @addr: block address (index of first block - 256B blocks)
> > + * @count: number of 256B blosks
> > + * @data: pointer to data to program
> > + *
> > + * Write a series of blocks to the RPMB device.
> > + *
> > + * Return:
> > + * *        0 on success
> > + * *        -EINVAL on wrong parameters
> > + * *        -EACCESS no key set
> > + * *        -EOPNOTSUPP if device doesn't support the requested operation
> > + * *        < 0 if the operation fails
> > + */
> > +int rpmb_write_blocks(struct rpmb_dev *rdev, key_serial_t keyid, int addr,
> > +                     int count, u8 *data)
> > +{
> > +       int err;
> > +
> > +       if (!rdev || !count || !data)
> > +               return -EINVAL;
> > +
> > +       mutex_lock(&rdev->lock);
> > +       err = -EOPNOTSUPP;
> > +       if (rdev->ops && rdev->ops->write_blocks) {
> > +               err = rdev->ops->write_blocks(rdev->dev.parent, rdev->target, keyid,
> > +                                             addr, count, data);
> > +       }
> > +       mutex_unlock(&rdev->lock);
> > +
> > +       return err;
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_write_blocks);
> > +
> > +/**
> > + * rpmb_read_blocks() - read data from RPMB device
> > + * @rdev: rpmb device
> > + * @addr: block address (index of first block - 256B blocks)
> > + * @count: number of 256B blocks
> > + * @data: pointer to data to read
> > + *
> > + * Read a series of one or more blocks from the RPMB device.
> > + *
> > + * Return:
> > + * *        0 on success
> > + * *        -EINVAL on wrong parameters
> > + * *        -EACCESS no key set
> > + * *        -EOPNOTSUPP if device doesn't support the requested operation
> > + * *        < 0 if the operation fails
> > + */
> > +int rpmb_read_blocks(struct rpmb_dev *rdev, int addr, int count, u8 *data)
> > +{
> > +       int err;
> > +
> > +       if (!rdev || !count || !data)
> > +               return -EINVAL;
> > +
> > +       mutex_lock(&rdev->lock);
> > +       err = -EOPNOTSUPP;
> > +       if (rdev->ops && rdev->ops->read_blocks) {
> > +               err = rdev->ops->read_blocks(rdev->dev.parent, rdev->target,
> > +                                            addr, count, data);
> > +       }
> > +       mutex_unlock(&rdev->lock);
> > +
> > +       return err;
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_read_blocks);
> > +
> > +
> > +static void rpmb_dev_release(struct device *dev)
> > +{
> > +       struct rpmb_dev *rdev = to_rpmb_dev(dev);
> > +
> > +       ida_simple_remove(&rpmb_ida, rdev->id);
> > +       kfree(rdev);
> > +}
> > +
> > +struct class rpmb_class = {
> > +       .name = "rpmb",
> > +       .owner = THIS_MODULE,
> > +       .dev_release = rpmb_dev_release,
> > +};
> > +EXPORT_SYMBOL(rpmb_class);
> > +
> > +/**
> > + * rpmb_dev_find_device() - return first matching rpmb device
> > + * @data: data for the match function
> > + * @match: the matching function
> > + *
> > + * Return: matching rpmb device or NULL on failure
> > + */
> > +static
> > +struct rpmb_dev *rpmb_dev_find_device(const void *data,
> > +                                     int (*match)(struct device *dev,
> > +                                                  const void *data))
> > +{
> > +       struct device *dev;
> > +
> > +       dev = class_find_device(&rpmb_class, NULL, data, match);
> > +
> > +       return dev ? to_rpmb_dev(dev) : NULL;
> > +}
> > +
> > +struct device_with_target {
> > +       const struct device *dev;
> > +       u8 target;
> > +};
> > +
> > +static int match_by_parent(struct device *dev, const void *data)
> > +{
> > +       const struct device_with_target *d = data;
> > +       struct rpmb_dev *rdev = to_rpmb_dev(dev);
> > +
> > +       return (d->dev && dev->parent == d->dev && rdev->target == d->target);
> > +}
> > +
> > +/**
> > + * rpmb_dev_find_by_device() - retrieve rpmb device from the parent device
> > + * @parent: parent device of the rpmb device
> > + * @target: RPMB target/region within the physical device
> > + *
> > + * Return: NULL if there is no rpmb device associated with the parent device
> > + */
> > +struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target)
> > +{
> > +       struct device_with_target t;
> > +
> > +       if (!parent)
> > +               return NULL;
> > +
> > +       t.dev = parent;
> > +       t.target = target;
> > +
> > +       return rpmb_dev_find_device(&t, match_by_parent);
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_dev_find_by_device);
> > +
> > +/**
> > + * rpmb_dev_unregister() - unregister RPMB partition from the RPMB subsystem
> > + * @rdev: the rpmb device to unregister
> > + * Return:
> > + * *        0 on success
> > + * *        -EINVAL on wrong parameters
> > + */
> > +int rpmb_dev_unregister(struct rpmb_dev *rdev)
> > +{
> > +       if (!rdev)
> > +               return -EINVAL;
> > +
> > +       mutex_lock(&rdev->lock);
> > +       device_del(&rdev->dev);
> > +       mutex_unlock(&rdev->lock);
> > +
> > +       rpmb_dev_put(rdev);
> > +
> > +       return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_dev_unregister);
> > +
> > +/**
> > + * rpmb_dev_unregister_by_device() - unregister RPMB partition
> > + *     from the RPMB subsystem
> > + * @dev: the parent device of the rpmb device
> > + * @target: RPMB target/region within the physical device
> > + * Return:
> > + * *        0 on success
> > + * *        -EINVAL on wrong parameters
> > + * *        -ENODEV if a device cannot be find.
> > + */
> > +int rpmb_dev_unregister_by_device(struct device *dev, u8 target)
> > +{
> > +       struct rpmb_dev *rdev;
> > +
> > +       if (!dev)
> > +               return -EINVAL;
> > +
> > +       rdev = rpmb_dev_find_by_device(dev, target);
> > +       if (!rdev) {
> > +               dev_warn(dev, "no disk found %s\n", dev_name(dev->parent));
> > +               return -ENODEV;
> > +       }
> > +
> > +       rpmb_dev_put(rdev);
> > +
> > +       return rpmb_dev_unregister(rdev);
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_dev_unregister_by_device);
> > +
> > +/**
> > + * rpmb_dev_get_drvdata() - driver data getter
> > + * @rdev: rpmb device
> > + *
> > + * Return: driver private data
> > + */
> > +void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev)
> > +{
> > +       return dev_get_drvdata(&rdev->dev);
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_dev_get_drvdata);
> > +
> > +/**
> > + * rpmb_dev_set_drvdata() - driver data setter
> > + * @rdev: rpmb device
> > + * @data: data to store
> > + */
> > +void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data)
> > +{
> > +       dev_set_drvdata(&rdev->dev, data);
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_dev_set_drvdata);
> > +
> > +/**
> > + * rpmb_dev_register - register RPMB partition with the RPMB subsystem
> > + * @dev: storage device of the rpmb device
> > + * @target: RPMB target/region within the physical device
> > + * @ops: device specific operations
> > + *
> > + * Return: a pointer to rpmb device
> > + */
> > +struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target,
> > +                                  const struct rpmb_ops *ops)
> > +{
> > +       struct rpmb_dev *rdev;
> > +       int id;
> > +       int ret;
> > +
> > +       if (!dev || !ops)
> > +               return ERR_PTR(-EINVAL);
> > +
> > +       if (!ops->program_key)
> > +               return ERR_PTR(-EINVAL);
> > +
> > +       if (!ops->get_capacity)
> > +               return ERR_PTR(-EINVAL);
> > +
> > +       if (!ops->get_write_count)
> > +               return ERR_PTR(-EINVAL);
> > +
> > +       if (!ops->write_blocks)
> > +               return ERR_PTR(-EINVAL);
> > +
> > +       if (!ops->read_blocks)
> > +               return ERR_PTR(-EINVAL);
> > +
> > +       if (ops->type == RPMB_TYPE_ANY || ops->type > RPMB_TYPE_MAX)
> > +               return ERR_PTR(-EINVAL);
> > +
> > +       rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
> > +       if (!rdev)
> > +               return ERR_PTR(-ENOMEM);
> > +
> > +       id = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL);
> > +       if (id < 0) {
> > +               ret = id;
> > +               goto exit;
> > +       }
> > +
> > +       mutex_init(&rdev->lock);
> > +       rdev->ops = ops;
> > +       rdev->id = id;
> > +       rdev->target = target;
> > +
> > +       dev_set_name(&rdev->dev, "rpmb%d", id);
> > +       rdev->dev.class = &rpmb_class;
> > +       rdev->dev.parent = dev;
> > +       ret = device_register(&rdev->dev);
> > +       if (ret)
> > +               goto exit;
> > +
> > +       dev_dbg(&rdev->dev, "registered device\n");
> > +
> > +       return rdev;
> > +
> > +exit:
> > +       if (id >= 0)
> > +               ida_simple_remove(&rpmb_ida, id);
> > +       kfree(rdev);
> > +       return ERR_PTR(ret);
> > +}
> > +EXPORT_SYMBOL_GPL(rpmb_dev_register);
> > +
> > +static int __init rpmb_init(void)
> > +{
> > +       ida_init(&rpmb_ida);
> > +       class_register(&rpmb_class);
> > +       return 0;
> > +}
> > +
> > +static void __exit rpmb_exit(void)
> > +{
> > +       class_unregister(&rpmb_class);
> > +       ida_destroy(&rpmb_ida);
> > +}
> > +
> > +subsys_initcall(rpmb_init);
> > +module_exit(rpmb_exit);
> > +
> > +MODULE_AUTHOR("Intel Corporation");
> > +MODULE_DESCRIPTION("RPMB class");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h
> > new file mode 100644
> > index 000000000000..718ba7c91ecd
> > --- /dev/null
> > +++ b/include/linux/rpmb.h
> > @@ -0,0 +1,163 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
> > +/*
> > + * Copyright (C) 2015-2019 Intel Corp. All rights reserved
> > + * Copyright (C) 2021 Linaro Ltd
> > + */
> > +#ifndef __RPMB_H__
> > +#define __RPMB_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/device.h>
> > +#include <linux/kref.h>
> > +#include <linux/key.h>
> > +
> > +/**
> > + * struct rpmb_ops - RPMB ops to be implemented by underlying block device
> > + *
> > + * @program_key    : program device key (once only op).
> > + * @get_capacity   : rpmb size in 128K units in for region/target.
> > + * @get_write_count: return the device write counter
> > + * @write_blocks   : write blocks to RPMB device
> > + * @read_blocks    : read blocks from RPMB device
> > + * @block_size     : block size in half sectors (1 == 256B)
> > + * @wr_cnt_max     : maximal number of blocks that can be
> > + *                   written in one access.
> > + * @rd_cnt_max     : maximal number of blocks that can be
> > + *                   read in one access.
> > + * @auth_method    : rpmb_auth_method
> > + * @dev_id         : unique device identifier
> > + * @dev_id_len     : unique device identifier length
> > + */
> > +struct rpmb_ops {
> > +       int (*program_key)(struct device *dev, u8 target, key_serial_t keyid);
> > +       int (*get_capacity)(struct device *dev, u8 target);
> > +       int (*get_write_count)(struct device *dev, u8 target);
> > +       int (*write_blocks)(struct device *dev, u8 target, key_serial_t keyid,
> > +                           int addr, int count, u8 *data);
> > +       int (*read_blocks)(struct device *dev, u8 target,
> > +                          int addr, int count, u8 *data);
> > +       u16 block_size;
> > +       u16 wr_cnt_max;
> > +       u16 rd_cnt_max;
> > +       u16 auth_method;
> > +       const u8 *dev_id;
> > +       size_t dev_id_len;
> > +};
> > +
> > +/**
> > + * struct rpmb_dev - device which can support RPMB partition
> > + *
> > + * @lock       : the device lock
> > + * @dev        : device
> > + * @id         : device id
> > + * @target     : RPMB target/region within the physical device
> > + * @ops        : operation exported by block layer
> > + */
> > +struct rpmb_dev {
> > +       struct mutex lock; /* device serialization lock */
> > +       struct device dev;
> > +       int id;
> > +       u8 target;
> > +       const struct rpmb_ops *ops;
> > +};
> > +
> > +#define to_rpmb_dev(x) container_of((x), struct rpmb_dev, dev)
> > +
> > +#if IS_ENABLED(CONFIG_RPMB)
> > +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev);
> > +void rpmb_dev_put(struct rpmb_dev *rdev);
> > +struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target);
> > +struct rpmb_dev *rpmb_dev_get_by_type(u32 type);
> > +struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target,
> > +                                  const struct rpmb_ops *ops);
> > +void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev);
> > +void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data);
> > +int rpmb_dev_unregister(struct rpmb_dev *rdev);
> > +int rpmb_dev_unregister_by_device(struct device *dev, u8 target);
> > +int rpmb_program_key(struct rpmb_dev *rdev, key_serial_t keyid);
> > +int rpmb_get_capacity(struct rpmb_dev *rdev);
> > +int rpmb_get_write_count(struct rpmb_dev *rdev);
> > +int rpmb_write_blocks(struct rpmb_dev *rdev, key_serial_t keyid,
> > +                     int addr, int count, u8 *data);
> > +int rpmb_read_blocks(struct rpmb_dev *rdev, int addr, int count, u8 *data);
> > +
> > +#else
> > +static inline struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev)
> > +{
> > +       return NULL;
> > +}
> > +
> > +static inline void rpmb_dev_put(struct rpmb_dev *rdev) { }
> > +
> > +static inline struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent,
> > +                                                      u8 target)
> > +{
> > +       return NULL;
> > +}
> > +
> > +static inline
> > +struct rpmb_dev *rpmb_dev_get_by_type(enum rpmb_type type)
> > +{
> > +       return NULL;
> > +}
> > +
> > +static inline void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev)
> > +{
> > +       return NULL;
> > +}
> > +
> > +static inline void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data)
> > +{
> > +}
> > +
> > +static inline struct rpmb_dev *
> > +rpmb_dev_register(struct device *dev, u8 target, const struct rpmb_ops *ops)
> > +{
> > +       return NULL;
> > +}
> > +
> > +static inline int rpmb_dev_unregister(struct rpmb_dev *dev)
> > +{
> > +       return 0;
> > +}
> > +
> > +static inline int rpmb_dev_unregister_by_device(struct device *dev, u8 target)
> > +{
> > +       return 0;
> > +}
> > +
> > +static inline int rpmb_program_key(struct rpmb_dev *rdev, key_serial_t keyid)
> > +{
> > +       return 0;
> > +}
> > +
> > +static inline rpmb_set_key(struct rpmb_dev *rdev, u8 *key, int keylen);
> > +{
> > +       return 0;
> > +}
> > +
> > +static inline int rpmb_get_capacity(struct rpmb_dev *rdev)
> > +{
> > +       return 0;
> > +}
> > +
> > +static inline int rpmb_get_write_count(struct rpmb_dev *rdev)
> > +{
> > +       return 0;
> > +}
> > +
> > +static inline int rpmb_write_blocks(struct rpmb_dev *rdev, int addr, int count,
> > +                                   u8 *data)
> > +{
> > +       return 0;
> > +}
> > +
> > +static inline int rpmb_read_blocks(struct rpmb_dev *rdev, int addr, int count,
> > +                                  u8 *data)
> > +{
> > +       return 0;
> > +}
> > +
> > +#endif /* CONFIG_RPMB */
> > +
> > +#endif /* __RPMB_H__ */
> > --
> > 2.20.1
> >



-- 
Alex Bennée
KVM/QEMU Hacker for Linaro

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

* RE: [RFC PATCH  2/5] char: rpmb: provide a user space interface
  2021-03-03 13:54 ` [RFC PATCH 2/5] char: rpmb: provide a user space interface Alex Bennée
@ 2021-03-04  7:01   ` Winkler, Tomas
  2021-03-04 10:19     ` Alex Bennée
  2021-03-04 21:08   ` Arnd Bergmann
  1 sibling, 1 reply; 47+ messages in thread
From: Winkler, Tomas @ 2021-03-04  7:01 UTC (permalink / raw)
  To: Alex Bennée, linux-kernel
  Cc: maxim.uvarov, joakim.bech, ilias.apalodimas, arnd, ruchika.gupta,
	Huang, Yang, Zhu, Bing, Matti.Moell, hmo, linux-mmc, linux-scsi,
	linux-nvme, Ulf Hansson, Linus Walleij, Arnd Bergmann, Usyskin,
	Alexander, Avri Altman


> 
> The user space API is achieved via a number of synchronous IOCTLs.
> 
>   * RPMB_IOC_VER_CMD - simple versioning API
>   * RPMB_IOC_CAP_CMD - query of underlying capabilities
>   * RPMB_IOC_PKEY_CMD - one time programming of access key
>   * RPMB_IOC_COUNTER_CMD - query the write counter
>   * RPMB_IOC_WBLOCKS_CMD - write blocks to device
>   * RPMB_IOC_RBLOCKS_CMD - read blocks from device
> 
> The keys used for programming and writing blocks to the device are
> key_serial_t handles as provided by the keyctl() interface.
> 
> [AJB: here there are two key differences between this and the original
> proposal. The first is the dropping of the sequence of preformated frames in
> favour of explicit actions. The second is the introduction of key_serial_t and
> the keyring API for referencing the key to use]

Putting it gently I'm not sure this is good idea, from the security point of view.
The key has to be possession of the one that signs the frames as they are, it doesn't mean it is linux kernel keyring, it can be other party on different system.
With this approach you will make the other usecases not applicable. It is less then trivial to move key securely from one system to another.
We all wished it can be abstracted more but the frames has to come already signed, and the key material is inside the frame. 
Thanks
Tomas


> 
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> Cc: Ulf Hansson <ulf.hansson@linaro.org>
> Cc: Linus  Walleij <linus.walleij@linaro.org>
> Cc: Arnd Bergmann <arnd.bergmann@linaro.org>
> Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> Cc: Tomas Winkler <tomas.winkler@intel.com>
> Cc: Alexander Usyskin <alexander.usyskin@intel.com>
> Cc: Avri Altman <avri.altman@sandisk.com>
> ---
>  .../userspace-api/ioctl/ioctl-number.rst      |   1 +
>  MAINTAINERS                                   |   1 +
>  drivers/char/rpmb/Kconfig                     |   7 +
>  drivers/char/rpmb/Makefile                    |   1 +
>  drivers/char/rpmb/cdev.c                      | 246 ++++++++++++++++++
>  drivers/char/rpmb/core.c                      |  10 +-
>  drivers/char/rpmb/rpmb-cdev.h                 |  17 ++
>  include/linux/rpmb.h                          |  10 +
>  include/uapi/linux/rpmb.h                     |  68 +++++
>  9 files changed, 357 insertions(+), 4 deletions(-)  create mode 100644
> drivers/char/rpmb/cdev.c  create mode 100644 drivers/char/rpmb/rpmb-
> cdev.h  create mode 100644 include/uapi/linux/rpmb.h
> 
> diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst
> b/Documentation/userspace-api/ioctl/ioctl-number.rst
> index a4c75a28c839..0ff2d4d81bb0 100644
> --- a/Documentation/userspace-api/ioctl/ioctl-number.rst
> +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
> @@ -344,6 +344,7 @@ Code  Seq#    Include File
> Comments
>  0xB5  00-0F  uapi/linux/rpmsg.h                                      <mailto:linux-
> remoteproc@vger.kernel.org>
>  0xB6  all    linux/fpga-dfl.h
>  0xB7  all    uapi/linux/remoteproc_cdev.h                            <mailto:linux-
> remoteproc@vger.kernel.org>
> +0xB8  80-8F  uapi/linux/rpmb.h                                       <mailto:linux-
> mmc@vger.kernel.org>
>  0xC0  00-0F  linux/usb/iowarrior.h
>  0xCA  00-0F  uapi/misc/cxl.h
>  0xCA  10-2F  uapi/misc/ocxl.h
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 076f3983526c..c60b41b6e6bd 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15374,6 +15374,7 @@ M:	?
>  L:	linux-kernel@vger.kernel.org
>  S:	Supported
>  F:	drivers/char/rpmb/*
> +F:	include/uapi/linux/rpmb.h
>  F:	include/linux/rpmb.h
> 
>  RTL2830 MEDIA DRIVER
> diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig index
> 431c2823cf70..9068664a399a 100644
> --- a/drivers/char/rpmb/Kconfig
> +++ b/drivers/char/rpmb/Kconfig
> @@ -9,3 +9,10 @@ config RPMB
>  	  access RPMB partition.
> 
>  	  If unsure, select N.
> +
> +config RPMB_INTF_DEV
> +	bool "RPMB character device interface /dev/rpmbN"
> +	depends on RPMB && KEYS
> +	help
> +	  Say yes here if you want to access RPMB from user space
> +	  via character device interface /dev/rpmb%d
> diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile index
> 24d4752a9a53..f54b3f30514b 100644
> --- a/drivers/char/rpmb/Makefile
> +++ b/drivers/char/rpmb/Makefile
> @@ -3,5 +3,6 @@
> 
>  obj-$(CONFIG_RPMB) += rpmb.o
>  rpmb-objs += core.o
> +rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o
> 
>  ccflags-y += -D__CHECK_ENDIAN__
> diff --git a/drivers/char/rpmb/cdev.c b/drivers/char/rpmb/cdev.c new file
> mode 100644 index 000000000000..55f66720fd03
> --- /dev/null
> +++ b/drivers/char/rpmb/cdev.c
> @@ -0,0 +1,246 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright(c) 2015 - 2019 Intel Corporation.
> + */
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/fs.h>
> +#include <linux/uaccess.h>
> +#include <linux/compat.h>
> +#include <linux/slab.h>
> +#include <linux/capability.h>
> +
> +#include <linux/rpmb.h>
> +
> +#include "rpmb-cdev.h"
> +
> +static dev_t rpmb_devt;
> +#define RPMB_MAX_DEVS  MINORMASK
> +
> +#define RPMB_DEV_OPEN    0  /** single open bit (position) */
> +
> +/**
> + * rpmb_open - the open function
> + *
> + * @inode: pointer to inode structure
> + * @fp: pointer to file structure
> + *
> + * Return: 0 on success, <0 on error
> + */
> +static int rpmb_open(struct inode *inode, struct file *fp) {
> +	struct rpmb_dev *rdev;
> +
> +	rdev = container_of(inode->i_cdev, struct rpmb_dev, cdev);
> +	if (!rdev)
> +		return -ENODEV;
> +
> +	/* the rpmb is single open! */
> +	if (test_and_set_bit(RPMB_DEV_OPEN, &rdev->status))
> +		return -EBUSY;
> +
> +	mutex_lock(&rdev->lock);
> +
> +	fp->private_data = rdev;
> +
> +	mutex_unlock(&rdev->lock);
> +
> +	return nonseekable_open(inode, fp);
> +}
> +
> +/**
> + * rpmb_release - the cdev release function
> + *
> + * @inode: pointer to inode structure
> + * @fp: pointer to file structure
> + *
> + * Return: 0 always.
> + */
> +static int rpmb_release(struct inode *inode, struct file *fp) {
> +	struct rpmb_dev *rdev = fp->private_data;
> +
> +	clear_bit(RPMB_DEV_OPEN, &rdev->status);
> +
> +	return 0;
> +}
> +
> +static long rpmb_ioctl_ver_cmd(struct rpmb_dev *rdev,
> +			       struct rpmb_ioc_ver_cmd __user *ptr) {
> +	struct rpmb_ioc_ver_cmd ver = {
> +		.api_version = RPMB_API_VERSION,
> +	};
> +
> +	return copy_to_user(ptr, &ver, sizeof(ver)) ? -EFAULT : 0; }
> +
> +static long rpmb_ioctl_cap_cmd(struct rpmb_dev *rdev,
> +			       struct rpmb_ioc_cap_cmd __user *ptr) {
> +	struct rpmb_ioc_cap_cmd cap;
> +
> +	cap.target      = rdev->target;
> +	cap.block_size  = rdev->ops->block_size;
> +	cap.wr_cnt_max  = rdev->ops->wr_cnt_max;
> +	cap.rd_cnt_max  = rdev->ops->rd_cnt_max;
> +	cap.auth_method = rdev->ops->auth_method;
> +	cap.capacity    = rpmb_get_capacity(rdev);
> +	cap.reserved    = 0;
> +
> +	return copy_to_user(ptr, &cap, sizeof(cap)) ? -EFAULT : 0; }
> +
> +static long rpmb_ioctl_pkey_cmd(struct rpmb_dev *rdev, key_serial_t
> +__user *k) {
> +	key_serial_t keyid;
> +
> +	if (get_user(keyid, k))
> +		return -EFAULT;
> +	else
> +		return rpmb_program_key(rdev, keyid); }
> +
> +static long rpmb_ioctl_counter_cmd(struct rpmb_dev *rdev, int __user
> +*ptr) {
> +	int count = rpmb_get_write_count(rdev);
> +
> +	if (count > 0)
> +		return put_user(count, ptr);
> +	else
> +		return count;
> +}
> +
> +static long rpmb_ioctl_wblocks_cmd(struct rpmb_dev *rdev,
> +				   struct rpmb_ioc_blocks_cmd __user *ptr) {
> +	struct rpmb_ioc_blocks_cmd wblocks;
> +	int sz;
> +	long ret;
> +	u8 *data;
> +
> +	if (copy_from_user(&wblocks, ptr, sizeof(struct
> rpmb_ioc_blocks_cmd)))
> +		return -EFAULT;
> +
> +	/* Don't write more blocks device supports */
> +	if (wblocks.count > rdev->ops->wr_cnt_max)
> +		return -EINVAL;
> +
> +	sz = wblocks.count * 256;
> +	data = kmalloc(sz, GFP_KERNEL);
> +
> +	if (!data)
> +		return -ENOMEM;
> +
> +	if (copy_from_user(data, wblocks.data, sz))
> +		ret = -EFAULT;
> +	else
> +		ret = rpmb_write_blocks(rdev, wblocks.key, wblocks.addr,
> +wblocks.count, data);
> +
> +	kfree(data);
> +	return ret;
> +}
> +
> +static long rpmb_ioctl_rblocks_cmd(struct rpmb_dev *rdev,
> +				   struct rpmb_ioc_blocks_cmd __user *ptr) {
> +	struct rpmb_ioc_blocks_cmd rblocks;
> +	int sz;
> +	long ret;
> +	u8 *data;
> +
> +	if (copy_from_user(&rblocks, ptr, sizeof(struct
> rpmb_ioc_blocks_cmd)))
> +		return -EFAULT;
> +
> +	if (rblocks.count > rdev->ops->rd_cnt_max)
> +		return -EINVAL;
> +
> +	sz = rblocks.count * 256;
> +	data = kmalloc(sz, GFP_KERNEL);
> +
> +	if (!data)
> +		return -ENOMEM;
> +
> +	ret = rpmb_read_blocks(rdev, rblocks.addr, rblocks.count, data);
> +
> +	if (ret == 0)
> +		ret = copy_to_user(rblocks.data, data, sz);
> +
> +	kfree(data);
> +	return ret;
> +}
> +
> +/**
> + * rpmb_ioctl - rpmb ioctl dispatcher
> + *
> + * @fp: a file pointer
> + * @cmd: ioctl command RPMB_IOC_SEQ_CMD RPMB_IOC_VER_CMD
> +RPMB_IOC_CAP_CMD
> + * @arg: ioctl data: rpmb_ioc_ver_cmd rpmb_ioc_cap_cmd
> pmb_ioc_seq_cmd
> + *
> + * Return: 0 on success; < 0 on error
> + */
> +static long rpmb_ioctl(struct file *fp, unsigned int cmd, unsigned long
> +arg) {
> +	struct rpmb_dev *rdev = fp->private_data;
> +	void __user *ptr = (void __user *)arg;
> +
> +	switch (cmd) {
> +	case RPMB_IOC_VER_CMD:
> +		return rpmb_ioctl_ver_cmd(rdev, ptr);
> +	case RPMB_IOC_CAP_CMD:
> +		return rpmb_ioctl_cap_cmd(rdev, ptr);
> +	case RPMB_IOC_PKEY_CMD:
> +		return rpmb_ioctl_pkey_cmd(rdev, ptr);
> +	case RPMB_IOC_COUNTER_CMD:
> +		return rpmb_ioctl_counter_cmd(rdev, ptr);
> +	case RPMB_IOC_WBLOCKS_CMD:
> +		return rpmb_ioctl_wblocks_cmd(rdev, ptr);
> +	case RPMB_IOC_RBLOCKS_CMD:
> +		return rpmb_ioctl_rblocks_cmd(rdev, ptr);
> +	default:
> +		dev_err(&rdev->dev, "unsupported ioctl 0x%x.\n", cmd);
> +		return -ENOIOCTLCMD;
> +	}
> +}
> +
> +static const struct file_operations rpmb_fops = {
> +	.open           = rpmb_open,
> +	.release        = rpmb_release,
> +	.unlocked_ioctl = rpmb_ioctl,
> +	.owner          = THIS_MODULE,
> +	.llseek         = noop_llseek,
> +};
> +
> +void rpmb_cdev_prepare(struct rpmb_dev *rdev) {
> +	rdev->dev.devt = MKDEV(MAJOR(rpmb_devt), rdev->id);
> +	rdev->cdev.owner = THIS_MODULE;
> +	cdev_init(&rdev->cdev, &rpmb_fops);
> +}
> +
> +void rpmb_cdev_add(struct rpmb_dev *rdev) {
> +	cdev_add(&rdev->cdev, rdev->dev.devt, 1); }
> +
> +void rpmb_cdev_del(struct rpmb_dev *rdev) {
> +	if (rdev->dev.devt)
> +		cdev_del(&rdev->cdev);
> +}
> +
> +int __init rpmb_cdev_init(void)
> +{
> +	int ret;
> +
> +	ret = alloc_chrdev_region(&rpmb_devt, 0, RPMB_MAX_DEVS,
> "rpmb");
> +	if (ret < 0)
> +		pr_err("unable to allocate char dev region\n");
> +
> +	return ret;
> +}
> +
> +void __exit rpmb_cdev_exit(void)
> +{
> +	unregister_chrdev_region(rpmb_devt, RPMB_MAX_DEVS); }
> diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c index
> a2e21c14986a..e26d605e48e1 100644
> --- a/drivers/char/rpmb/core.c
> +++ b/drivers/char/rpmb/core.c
> @@ -12,6 +12,7 @@
>  #include <linux/slab.h>
> 
>  #include <linux/rpmb.h>
> +#include "rpmb-cdev.h"
> 
>  static DEFINE_IDA(rpmb_ida);
> 
> @@ -277,6 +278,7 @@ int rpmb_dev_unregister(struct rpmb_dev *rdev)
>  		return -EINVAL;
> 
>  	mutex_lock(&rdev->lock);
> +	rpmb_cdev_del(rdev);
>  	device_del(&rdev->dev);
>  	mutex_unlock(&rdev->lock);
> 
> @@ -371,9 +373,6 @@ struct rpmb_dev *rpmb_dev_register(struct device
> *dev, u8 target,
>  	if (!ops->read_blocks)
>  		return ERR_PTR(-EINVAL);
> 
> -	if (ops->type == RPMB_TYPE_ANY || ops->type >
> RPMB_TYPE_MAX)
> -		return ERR_PTR(-EINVAL);
> -
>  	rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
>  	if (!rdev)
>  		return ERR_PTR(-ENOMEM);
> @@ -396,6 +395,8 @@ struct rpmb_dev *rpmb_dev_register(struct device
> *dev, u8 target,
>  	if (ret)
>  		goto exit;
> 
> +	rpmb_cdev_add(rdev);
> +
>  	dev_dbg(&rdev->dev, "registered device\n");
> 
>  	return rdev;
> @@ -412,11 +413,12 @@ static int __init rpmb_init(void)  {
>  	ida_init(&rpmb_ida);
>  	class_register(&rpmb_class);
> -	return 0;
> +	return rpmb_cdev_init();
>  }
> 
>  static void __exit rpmb_exit(void)
>  {
> +	rpmb_cdev_exit();
>  	class_unregister(&rpmb_class);
>  	ida_destroy(&rpmb_ida);
>  }
> diff --git a/drivers/char/rpmb/rpmb-cdev.h b/drivers/char/rpmb/rpmb-
> cdev.h new file mode 100644 index 000000000000..e59ff0c05e9d
> --- /dev/null
> +++ b/drivers/char/rpmb/rpmb-cdev.h
> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
> +/*
> + * Copyright (C) 2015-2018 Intel Corp. All rights reserved  */ #ifdef
> +CONFIG_RPMB_INTF_DEV int __init rpmb_cdev_init(void); void __exit
> +rpmb_cdev_exit(void); void rpmb_cdev_prepare(struct rpmb_dev *rdev);
> +void rpmb_cdev_add(struct rpmb_dev *rdev); void rpmb_cdev_del(struct
> +rpmb_dev *rdev); #else static inline int __init rpmb_cdev_init(void) {
> +return 0; } static inline void __exit rpmb_cdev_exit(void) {} static
> +inline void rpmb_cdev_prepare(struct rpmb_dev *rdev) {} static inline
> +void rpmb_cdev_add(struct rpmb_dev *rdev) {} static inline void
> +rpmb_cdev_del(struct rpmb_dev *rdev) {} #endif /*
> CONFIG_RPMB_INTF_DEV
> +*/
> diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h index
> 718ba7c91ecd..fe44f60efe31 100644
> --- a/include/linux/rpmb.h
> +++ b/include/linux/rpmb.h
> @@ -8,9 +8,13 @@
> 
>  #include <linux/types.h>
>  #include <linux/device.h>
> +#include <linux/cdev.h>
> +#include <uapi/linux/rpmb.h>
>  #include <linux/kref.h>
>  #include <linux/key.h>
> 
> +#define RPMB_API_VERSION 0x80000001
> +
>  /**
>   * struct rpmb_ops - RPMB ops to be implemented by underlying block
> device
>   *
> @@ -51,6 +55,8 @@ struct rpmb_ops {
>   * @dev        : device
>   * @id         : device id
>   * @target     : RPMB target/region within the physical device
> + * @cdev       : character dev
> + * @status     : device status
>   * @ops        : operation exported by block layer
>   */
>  struct rpmb_dev {
> @@ -58,6 +64,10 @@ struct rpmb_dev {
>  	struct device dev;
>  	int id;
>  	u8 target;
> +#ifdef CONFIG_RPMB_INTF_DEV
> +	struct cdev cdev;
> +	unsigned long status;
> +#endif /* CONFIG_RPMB_INTF_DEV */
>  	const struct rpmb_ops *ops;
>  };
> 
> diff --git a/include/uapi/linux/rpmb.h b/include/uapi/linux/rpmb.h new file
> mode 100644 index 000000000000..3957b785cdd5
> --- /dev/null
> +++ b/include/uapi/linux/rpmb.h
> @@ -0,0 +1,68 @@
> +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR
> +BSD-3-Clause) */
> +/*
> + * Copyright (C) 2015-2018 Intel Corp. All rights reserved
> + * Copyright (C) 2021 Linaro Ltd
> + */
> +#ifndef _UAPI_LINUX_RPMB_H_
> +#define _UAPI_LINUX_RPMB_H_
> +
> +#include <linux/types.h>
> +
> +/**
> + * struct rpmb_ioc_ver_cmd - rpmb api version
> + *
> + * @api_version: rpmb API version.
> + */
> +struct rpmb_ioc_ver_cmd {
> +	__u32 api_version;
> +} __packed;
> +
> +enum rpmb_auth_method {
> +	RPMB_HMAC_ALGO_SHA_256 = 0,
> +};
> +
> +/**
> + * struct rpmb_ioc_cap_cmd - rpmb capabilities
> + *
> + * @target: rpmb target/region within RPMB partition.
> + * @capacity: storage capacity (in units of 128K)
> + * @block_size: storage data block size (in units of 256B)
> + * @wr_cnt_max: maximal number of block that can be written in a single
> request.
> + * @rd_cnt_max: maximal number of block that can be read in a single
> request.
> + * @auth_method: authentication method: currently always
> HMAC_SHA_256
> + * @reserved: reserved to align to 4 bytes.
> + */
> +struct rpmb_ioc_cap_cmd {
> +	__u16 target;
> +	__u16 capacity;
> +	__u16 block_size;
> +	__u16 wr_cnt_max;
> +	__u16 rd_cnt_max;
> +	__u16 auth_method;
> +	__u16 reserved;
> +} __attribute__((packed));
> +
> +/**
> + * struct rpmb_ioc_blocks_cmd - read/write blocks to/from RPMB
> + *
> + * @keyid: key_serial_t of key to use
> + * @addr: index into device (units of 256B blocks)
> + * @count: number of 256B blocks
> + * @data: pointer to data to write/read  */ struct rpmb_ioc_blocks_cmd
> +{
> +	__s32 key; /* key_serial_t */
> +	__u32 addr;
> +	__u32 count;
> +	__u8 __user *data;
> +} __attribute__((packed));
> +
> +
> +#define RPMB_IOC_VER_CMD     _IOR(0xB8, 80, struct rpmb_ioc_ver_cmd)
> +#define RPMB_IOC_CAP_CMD     _IOR(0xB8, 81, struct rpmb_ioc_cap_cmd)
> +#define RPMB_IOC_PKEY_CMD    _IOW(0xB8, 82, key_serial_t)
> +#define RPMB_IOC_COUNTER_CMD _IOR(0xB8, 84, int) #define
> +RPMB_IOC_WBLOCKS_CMD _IOW(0xB8, 85, struct rpmb_ioc_blocks_cmd)
> #define
> +RPMB_IOC_RBLOCKS_CMD _IOR(0xB8, 86, struct rpmb_ioc_blocks_cmd)
> +
> +#endif /* _UAPI_LINUX_RPMB_H_ */
> --
> 2.20.1


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

* Re: [RFC PATCH  2/5] char: rpmb: provide a user space interface
  2021-03-04  7:01   ` Winkler, Tomas
@ 2021-03-04 10:19     ` Alex Bennée
  2021-03-04 10:34       ` Winkler, Tomas
  0 siblings, 1 reply; 47+ messages in thread
From: Alex Bennée @ 2021-03-04 10:19 UTC (permalink / raw)
  To: Winkler, Tomas
  Cc: linux-kernel, maxim.uvarov, joakim.bech, ilias.apalodimas, arnd,
	ruchika.gupta, Huang, Yang, Zhu, Bing, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Linus Walleij,
	Arnd Bergmann, Usyskin, Alexander, Avri Altman

"Winkler, Tomas" <tomas.winkler@intel.com> writes:

>> The user space API is achieved via a number of synchronous IOCTLs.
>> 
>>   * RPMB_IOC_VER_CMD - simple versioning API
>>   * RPMB_IOC_CAP_CMD - query of underlying capabilities
>>   * RPMB_IOC_PKEY_CMD - one time programming of access key
>>   * RPMB_IOC_COUNTER_CMD - query the write counter
>>   * RPMB_IOC_WBLOCKS_CMD - write blocks to device
>>   * RPMB_IOC_RBLOCKS_CMD - read blocks from device
>> 
>> The keys used for programming and writing blocks to the device are
>> key_serial_t handles as provided by the keyctl() interface.
>> 
>> [AJB: here there are two key differences between this and the original
>> proposal. The first is the dropping of the sequence of preformated frames in
>> favour of explicit actions. The second is the introduction of key_serial_t and
>> the keyring API for referencing the key to use]
>
> Putting it gently I'm not sure this is good idea, from the security point of view.
> The key has to be possession of the one that signs the frames as they are, it doesn't mean it is linux kernel keyring, it can be other party on different system.
> With this approach you will make the other usecases not applicable. It
> is less then trivial to move key securely from one system to another.

OK I can understand the desire for such a use-case but it does constrain
the interface on the kernel with access to the hardware to purely
providing a pipe to the raw hardware while also having to expose the
details of the HW to userspace. Also doesn't this break down after a
PROGRAM_KEY event as the key will have had to traverse into the
"untrusted" kernel?

I wonder if virtio-rpmb may be of help here? You could wrap up up the
front-end in the security domain that has the keys although I don't know
how easy it would be for a backend to work with real hardware?

> We all wished it can be abstracted more but the frames has to come already signed, and the key material is inside the frame. 
> Thanks
> Tomas
>
>
>> 
>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>> Cc: Ulf Hansson <ulf.hansson@linaro.org>
>> Cc: Linus  Walleij <linus.walleij@linaro.org>
>> Cc: Arnd Bergmann <arnd.bergmann@linaro.org>
>> Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
>> Cc: Tomas Winkler <tomas.winkler@intel.com>
>> Cc: Alexander Usyskin <alexander.usyskin@intel.com>
>> Cc: Avri Altman <avri.altman@sandisk.com>
>> ---
>>  .../userspace-api/ioctl/ioctl-number.rst      |   1 +
>>  MAINTAINERS                                   |   1 +
>>  drivers/char/rpmb/Kconfig                     |   7 +
>>  drivers/char/rpmb/Makefile                    |   1 +
>>  drivers/char/rpmb/cdev.c                      | 246 ++++++++++++++++++
>>  drivers/char/rpmb/core.c                      |  10 +-
>>  drivers/char/rpmb/rpmb-cdev.h                 |  17 ++
>>  include/linux/rpmb.h                          |  10 +
>>  include/uapi/linux/rpmb.h                     |  68 +++++
>>  9 files changed, 357 insertions(+), 4 deletions(-)  create mode 100644
>> drivers/char/rpmb/cdev.c  create mode 100644 drivers/char/rpmb/rpmb-
>> cdev.h  create mode 100644 include/uapi/linux/rpmb.h
>> 
>> diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst
>> b/Documentation/userspace-api/ioctl/ioctl-number.rst
>> index a4c75a28c839..0ff2d4d81bb0 100644
>> --- a/Documentation/userspace-api/ioctl/ioctl-number.rst
>> +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
>> @@ -344,6 +344,7 @@ Code  Seq#    Include File
>> Comments
>>  0xB5  00-0F  uapi/linux/rpmsg.h                                      <mailto:linux-
>> remoteproc@vger.kernel.org>
>>  0xB6  all    linux/fpga-dfl.h
>>  0xB7  all    uapi/linux/remoteproc_cdev.h                            <mailto:linux-
>> remoteproc@vger.kernel.org>
>> +0xB8  80-8F  uapi/linux/rpmb.h                                       <mailto:linux-
>> mmc@vger.kernel.org>
>>  0xC0  00-0F  linux/usb/iowarrior.h
>>  0xCA  00-0F  uapi/misc/cxl.h
>>  0xCA  10-2F  uapi/misc/ocxl.h
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 076f3983526c..c60b41b6e6bd 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -15374,6 +15374,7 @@ M:	?
>>  L:	linux-kernel@vger.kernel.org
>>  S:	Supported
>>  F:	drivers/char/rpmb/*
>> +F:	include/uapi/linux/rpmb.h
>>  F:	include/linux/rpmb.h
>> 
>>  RTL2830 MEDIA DRIVER
>> diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig index
>> 431c2823cf70..9068664a399a 100644
>> --- a/drivers/char/rpmb/Kconfig
>> +++ b/drivers/char/rpmb/Kconfig
>> @@ -9,3 +9,10 @@ config RPMB
>>  	  access RPMB partition.
>> 
>>  	  If unsure, select N.
>> +
>> +config RPMB_INTF_DEV
>> +	bool "RPMB character device interface /dev/rpmbN"
>> +	depends on RPMB && KEYS
>> +	help
>> +	  Say yes here if you want to access RPMB from user space
>> +	  via character device interface /dev/rpmb%d
>> diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile index
>> 24d4752a9a53..f54b3f30514b 100644
>> --- a/drivers/char/rpmb/Makefile
>> +++ b/drivers/char/rpmb/Makefile
>> @@ -3,5 +3,6 @@
>> 
>>  obj-$(CONFIG_RPMB) += rpmb.o
>>  rpmb-objs += core.o
>> +rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o
>> 
>>  ccflags-y += -D__CHECK_ENDIAN__
>> diff --git a/drivers/char/rpmb/cdev.c b/drivers/char/rpmb/cdev.c new file
>> mode 100644 index 000000000000..55f66720fd03
>> --- /dev/null
>> +++ b/drivers/char/rpmb/cdev.c
>> @@ -0,0 +1,246 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright(c) 2015 - 2019 Intel Corporation.
>> + */
>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>> +
>> +#include <linux/fs.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/compat.h>
>> +#include <linux/slab.h>
>> +#include <linux/capability.h>
>> +
>> +#include <linux/rpmb.h>
>> +
>> +#include "rpmb-cdev.h"
>> +
>> +static dev_t rpmb_devt;
>> +#define RPMB_MAX_DEVS  MINORMASK
>> +
>> +#define RPMB_DEV_OPEN    0  /** single open bit (position) */
>> +
>> +/**
>> + * rpmb_open - the open function
>> + *
>> + * @inode: pointer to inode structure
>> + * @fp: pointer to file structure
>> + *
>> + * Return: 0 on success, <0 on error
>> + */
>> +static int rpmb_open(struct inode *inode, struct file *fp) {
>> +	struct rpmb_dev *rdev;
>> +
>> +	rdev = container_of(inode->i_cdev, struct rpmb_dev, cdev);
>> +	if (!rdev)
>> +		return -ENODEV;
>> +
>> +	/* the rpmb is single open! */
>> +	if (test_and_set_bit(RPMB_DEV_OPEN, &rdev->status))
>> +		return -EBUSY;
>> +
>> +	mutex_lock(&rdev->lock);
>> +
>> +	fp->private_data = rdev;
>> +
>> +	mutex_unlock(&rdev->lock);
>> +
>> +	return nonseekable_open(inode, fp);
>> +}
>> +
>> +/**
>> + * rpmb_release - the cdev release function
>> + *
>> + * @inode: pointer to inode structure
>> + * @fp: pointer to file structure
>> + *
>> + * Return: 0 always.
>> + */
>> +static int rpmb_release(struct inode *inode, struct file *fp) {
>> +	struct rpmb_dev *rdev = fp->private_data;
>> +
>> +	clear_bit(RPMB_DEV_OPEN, &rdev->status);
>> +
>> +	return 0;
>> +}
>> +
>> +static long rpmb_ioctl_ver_cmd(struct rpmb_dev *rdev,
>> +			       struct rpmb_ioc_ver_cmd __user *ptr) {
>> +	struct rpmb_ioc_ver_cmd ver = {
>> +		.api_version = RPMB_API_VERSION,
>> +	};
>> +
>> +	return copy_to_user(ptr, &ver, sizeof(ver)) ? -EFAULT : 0; }
>> +
>> +static long rpmb_ioctl_cap_cmd(struct rpmb_dev *rdev,
>> +			       struct rpmb_ioc_cap_cmd __user *ptr) {
>> +	struct rpmb_ioc_cap_cmd cap;
>> +
>> +	cap.target      = rdev->target;
>> +	cap.block_size  = rdev->ops->block_size;
>> +	cap.wr_cnt_max  = rdev->ops->wr_cnt_max;
>> +	cap.rd_cnt_max  = rdev->ops->rd_cnt_max;
>> +	cap.auth_method = rdev->ops->auth_method;
>> +	cap.capacity    = rpmb_get_capacity(rdev);
>> +	cap.reserved    = 0;
>> +
>> +	return copy_to_user(ptr, &cap, sizeof(cap)) ? -EFAULT : 0; }
>> +
>> +static long rpmb_ioctl_pkey_cmd(struct rpmb_dev *rdev, key_serial_t
>> +__user *k) {
>> +	key_serial_t keyid;
>> +
>> +	if (get_user(keyid, k))
>> +		return -EFAULT;
>> +	else
>> +		return rpmb_program_key(rdev, keyid); }
>> +
>> +static long rpmb_ioctl_counter_cmd(struct rpmb_dev *rdev, int __user
>> +*ptr) {
>> +	int count = rpmb_get_write_count(rdev);
>> +
>> +	if (count > 0)
>> +		return put_user(count, ptr);
>> +	else
>> +		return count;
>> +}
>> +
>> +static long rpmb_ioctl_wblocks_cmd(struct rpmb_dev *rdev,
>> +				   struct rpmb_ioc_blocks_cmd __user *ptr) {
>> +	struct rpmb_ioc_blocks_cmd wblocks;
>> +	int sz;
>> +	long ret;
>> +	u8 *data;
>> +
>> +	if (copy_from_user(&wblocks, ptr, sizeof(struct
>> rpmb_ioc_blocks_cmd)))
>> +		return -EFAULT;
>> +
>> +	/* Don't write more blocks device supports */
>> +	if (wblocks.count > rdev->ops->wr_cnt_max)
>> +		return -EINVAL;
>> +
>> +	sz = wblocks.count * 256;
>> +	data = kmalloc(sz, GFP_KERNEL);
>> +
>> +	if (!data)
>> +		return -ENOMEM;
>> +
>> +	if (copy_from_user(data, wblocks.data, sz))
>> +		ret = -EFAULT;
>> +	else
>> +		ret = rpmb_write_blocks(rdev, wblocks.key, wblocks.addr,
>> +wblocks.count, data);
>> +
>> +	kfree(data);
>> +	return ret;
>> +}
>> +
>> +static long rpmb_ioctl_rblocks_cmd(struct rpmb_dev *rdev,
>> +				   struct rpmb_ioc_blocks_cmd __user *ptr) {
>> +	struct rpmb_ioc_blocks_cmd rblocks;
>> +	int sz;
>> +	long ret;
>> +	u8 *data;
>> +
>> +	if (copy_from_user(&rblocks, ptr, sizeof(struct
>> rpmb_ioc_blocks_cmd)))
>> +		return -EFAULT;
>> +
>> +	if (rblocks.count > rdev->ops->rd_cnt_max)
>> +		return -EINVAL;
>> +
>> +	sz = rblocks.count * 256;
>> +	data = kmalloc(sz, GFP_KERNEL);
>> +
>> +	if (!data)
>> +		return -ENOMEM;
>> +
>> +	ret = rpmb_read_blocks(rdev, rblocks.addr, rblocks.count, data);
>> +
>> +	if (ret == 0)
>> +		ret = copy_to_user(rblocks.data, data, sz);
>> +
>> +	kfree(data);
>> +	return ret;
>> +}
>> +
>> +/**
>> + * rpmb_ioctl - rpmb ioctl dispatcher
>> + *
>> + * @fp: a file pointer
>> + * @cmd: ioctl command RPMB_IOC_SEQ_CMD RPMB_IOC_VER_CMD
>> +RPMB_IOC_CAP_CMD
>> + * @arg: ioctl data: rpmb_ioc_ver_cmd rpmb_ioc_cap_cmd
>> pmb_ioc_seq_cmd
>> + *
>> + * Return: 0 on success; < 0 on error
>> + */
>> +static long rpmb_ioctl(struct file *fp, unsigned int cmd, unsigned long
>> +arg) {
>> +	struct rpmb_dev *rdev = fp->private_data;
>> +	void __user *ptr = (void __user *)arg;
>> +
>> +	switch (cmd) {
>> +	case RPMB_IOC_VER_CMD:
>> +		return rpmb_ioctl_ver_cmd(rdev, ptr);
>> +	case RPMB_IOC_CAP_CMD:
>> +		return rpmb_ioctl_cap_cmd(rdev, ptr);
>> +	case RPMB_IOC_PKEY_CMD:
>> +		return rpmb_ioctl_pkey_cmd(rdev, ptr);
>> +	case RPMB_IOC_COUNTER_CMD:
>> +		return rpmb_ioctl_counter_cmd(rdev, ptr);
>> +	case RPMB_IOC_WBLOCKS_CMD:
>> +		return rpmb_ioctl_wblocks_cmd(rdev, ptr);
>> +	case RPMB_IOC_RBLOCKS_CMD:
>> +		return rpmb_ioctl_rblocks_cmd(rdev, ptr);
>> +	default:
>> +		dev_err(&rdev->dev, "unsupported ioctl 0x%x.\n", cmd);
>> +		return -ENOIOCTLCMD;
>> +	}
>> +}
>> +
>> +static const struct file_operations rpmb_fops = {
>> +	.open           = rpmb_open,
>> +	.release        = rpmb_release,
>> +	.unlocked_ioctl = rpmb_ioctl,
>> +	.owner          = THIS_MODULE,
>> +	.llseek         = noop_llseek,
>> +};
>> +
>> +void rpmb_cdev_prepare(struct rpmb_dev *rdev) {
>> +	rdev->dev.devt = MKDEV(MAJOR(rpmb_devt), rdev->id);
>> +	rdev->cdev.owner = THIS_MODULE;
>> +	cdev_init(&rdev->cdev, &rpmb_fops);
>> +}
>> +
>> +void rpmb_cdev_add(struct rpmb_dev *rdev) {
>> +	cdev_add(&rdev->cdev, rdev->dev.devt, 1); }
>> +
>> +void rpmb_cdev_del(struct rpmb_dev *rdev) {
>> +	if (rdev->dev.devt)
>> +		cdev_del(&rdev->cdev);
>> +}
>> +
>> +int __init rpmb_cdev_init(void)
>> +{
>> +	int ret;
>> +
>> +	ret = alloc_chrdev_region(&rpmb_devt, 0, RPMB_MAX_DEVS,
>> "rpmb");
>> +	if (ret < 0)
>> +		pr_err("unable to allocate char dev region\n");
>> +
>> +	return ret;
>> +}
>> +
>> +void __exit rpmb_cdev_exit(void)
>> +{
>> +	unregister_chrdev_region(rpmb_devt, RPMB_MAX_DEVS); }
>> diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c index
>> a2e21c14986a..e26d605e48e1 100644
>> --- a/drivers/char/rpmb/core.c
>> +++ b/drivers/char/rpmb/core.c
>> @@ -12,6 +12,7 @@
>>  #include <linux/slab.h>
>> 
>>  #include <linux/rpmb.h>
>> +#include "rpmb-cdev.h"
>> 
>>  static DEFINE_IDA(rpmb_ida);
>> 
>> @@ -277,6 +278,7 @@ int rpmb_dev_unregister(struct rpmb_dev *rdev)
>>  		return -EINVAL;
>> 
>>  	mutex_lock(&rdev->lock);
>> +	rpmb_cdev_del(rdev);
>>  	device_del(&rdev->dev);
>>  	mutex_unlock(&rdev->lock);
>> 
>> @@ -371,9 +373,6 @@ struct rpmb_dev *rpmb_dev_register(struct device
>> *dev, u8 target,
>>  	if (!ops->read_blocks)
>>  		return ERR_PTR(-EINVAL);
>> 
>> -	if (ops->type == RPMB_TYPE_ANY || ops->type >
>> RPMB_TYPE_MAX)
>> -		return ERR_PTR(-EINVAL);
>> -
>>  	rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
>>  	if (!rdev)
>>  		return ERR_PTR(-ENOMEM);
>> @@ -396,6 +395,8 @@ struct rpmb_dev *rpmb_dev_register(struct device
>> *dev, u8 target,
>>  	if (ret)
>>  		goto exit;
>> 
>> +	rpmb_cdev_add(rdev);
>> +
>>  	dev_dbg(&rdev->dev, "registered device\n");
>> 
>>  	return rdev;
>> @@ -412,11 +413,12 @@ static int __init rpmb_init(void)  {
>>  	ida_init(&rpmb_ida);
>>  	class_register(&rpmb_class);
>> -	return 0;
>> +	return rpmb_cdev_init();
>>  }
>> 
>>  static void __exit rpmb_exit(void)
>>  {
>> +	rpmb_cdev_exit();
>>  	class_unregister(&rpmb_class);
>>  	ida_destroy(&rpmb_ida);
>>  }
>> diff --git a/drivers/char/rpmb/rpmb-cdev.h b/drivers/char/rpmb/rpmb-
>> cdev.h new file mode 100644 index 000000000000..e59ff0c05e9d
>> --- /dev/null
>> +++ b/drivers/char/rpmb/rpmb-cdev.h
>> @@ -0,0 +1,17 @@
>> +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
>> +/*
>> + * Copyright (C) 2015-2018 Intel Corp. All rights reserved  */ #ifdef
>> +CONFIG_RPMB_INTF_DEV int __init rpmb_cdev_init(void); void __exit
>> +rpmb_cdev_exit(void); void rpmb_cdev_prepare(struct rpmb_dev *rdev);
>> +void rpmb_cdev_add(struct rpmb_dev *rdev); void rpmb_cdev_del(struct
>> +rpmb_dev *rdev); #else static inline int __init rpmb_cdev_init(void) {
>> +return 0; } static inline void __exit rpmb_cdev_exit(void) {} static
>> +inline void rpmb_cdev_prepare(struct rpmb_dev *rdev) {} static inline
>> +void rpmb_cdev_add(struct rpmb_dev *rdev) {} static inline void
>> +rpmb_cdev_del(struct rpmb_dev *rdev) {} #endif /*
>> CONFIG_RPMB_INTF_DEV
>> +*/
>> diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h index
>> 718ba7c91ecd..fe44f60efe31 100644
>> --- a/include/linux/rpmb.h
>> +++ b/include/linux/rpmb.h
>> @@ -8,9 +8,13 @@
>> 
>>  #include <linux/types.h>
>>  #include <linux/device.h>
>> +#include <linux/cdev.h>
>> +#include <uapi/linux/rpmb.h>
>>  #include <linux/kref.h>
>>  #include <linux/key.h>
>> 
>> +#define RPMB_API_VERSION 0x80000001
>> +
>>  /**
>>   * struct rpmb_ops - RPMB ops to be implemented by underlying block
>> device
>>   *
>> @@ -51,6 +55,8 @@ struct rpmb_ops {
>>   * @dev        : device
>>   * @id         : device id
>>   * @target     : RPMB target/region within the physical device
>> + * @cdev       : character dev
>> + * @status     : device status
>>   * @ops        : operation exported by block layer
>>   */
>>  struct rpmb_dev {
>> @@ -58,6 +64,10 @@ struct rpmb_dev {
>>  	struct device dev;
>>  	int id;
>>  	u8 target;
>> +#ifdef CONFIG_RPMB_INTF_DEV
>> +	struct cdev cdev;
>> +	unsigned long status;
>> +#endif /* CONFIG_RPMB_INTF_DEV */
>>  	const struct rpmb_ops *ops;
>>  };
>> 
>> diff --git a/include/uapi/linux/rpmb.h b/include/uapi/linux/rpmb.h new file
>> mode 100644 index 000000000000..3957b785cdd5
>> --- /dev/null
>> +++ b/include/uapi/linux/rpmb.h
>> @@ -0,0 +1,68 @@
>> +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR
>> +BSD-3-Clause) */
>> +/*
>> + * Copyright (C) 2015-2018 Intel Corp. All rights reserved
>> + * Copyright (C) 2021 Linaro Ltd
>> + */
>> +#ifndef _UAPI_LINUX_RPMB_H_
>> +#define _UAPI_LINUX_RPMB_H_
>> +
>> +#include <linux/types.h>
>> +
>> +/**
>> + * struct rpmb_ioc_ver_cmd - rpmb api version
>> + *
>> + * @api_version: rpmb API version.
>> + */
>> +struct rpmb_ioc_ver_cmd {
>> +	__u32 api_version;
>> +} __packed;
>> +
>> +enum rpmb_auth_method {
>> +	RPMB_HMAC_ALGO_SHA_256 = 0,
>> +};
>> +
>> +/**
>> + * struct rpmb_ioc_cap_cmd - rpmb capabilities
>> + *
>> + * @target: rpmb target/region within RPMB partition.
>> + * @capacity: storage capacity (in units of 128K)
>> + * @block_size: storage data block size (in units of 256B)
>> + * @wr_cnt_max: maximal number of block that can be written in a single
>> request.
>> + * @rd_cnt_max: maximal number of block that can be read in a single
>> request.
>> + * @auth_method: authentication method: currently always
>> HMAC_SHA_256
>> + * @reserved: reserved to align to 4 bytes.
>> + */
>> +struct rpmb_ioc_cap_cmd {
>> +	__u16 target;
>> +	__u16 capacity;
>> +	__u16 block_size;
>> +	__u16 wr_cnt_max;
>> +	__u16 rd_cnt_max;
>> +	__u16 auth_method;
>> +	__u16 reserved;
>> +} __attribute__((packed));
>> +
>> +/**
>> + * struct rpmb_ioc_blocks_cmd - read/write blocks to/from RPMB
>> + *
>> + * @keyid: key_serial_t of key to use
>> + * @addr: index into device (units of 256B blocks)
>> + * @count: number of 256B blocks
>> + * @data: pointer to data to write/read  */ struct rpmb_ioc_blocks_cmd
>> +{
>> +	__s32 key; /* key_serial_t */
>> +	__u32 addr;
>> +	__u32 count;
>> +	__u8 __user *data;
>> +} __attribute__((packed));
>> +
>> +
>> +#define RPMB_IOC_VER_CMD     _IOR(0xB8, 80, struct rpmb_ioc_ver_cmd)
>> +#define RPMB_IOC_CAP_CMD     _IOR(0xB8, 81, struct rpmb_ioc_cap_cmd)
>> +#define RPMB_IOC_PKEY_CMD    _IOW(0xB8, 82, key_serial_t)
>> +#define RPMB_IOC_COUNTER_CMD _IOR(0xB8, 84, int) #define
>> +RPMB_IOC_WBLOCKS_CMD _IOW(0xB8, 85, struct rpmb_ioc_blocks_cmd)
>> #define
>> +RPMB_IOC_RBLOCKS_CMD _IOR(0xB8, 86, struct rpmb_ioc_blocks_cmd)
>> +
>> +#endif /* _UAPI_LINUX_RPMB_H_ */
>> --
>> 2.20.1
>

-- 
Alex Bennée

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

* RE: [RFC PATCH  2/5] char: rpmb: provide a user space interface
  2021-03-04 10:19     ` Alex Bennée
@ 2021-03-04 10:34       ` Winkler, Tomas
  2021-03-04 17:52         ` Alex Bennée
  0 siblings, 1 reply; 47+ messages in thread
From: Winkler, Tomas @ 2021-03-04 10:34 UTC (permalink / raw)
  To: Alex Bennée
  Cc: linux-kernel, maxim.uvarov, joakim.bech, ilias.apalodimas, arnd,
	ruchika.gupta, Huang, Yang, Zhu, Bing, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Linus Walleij,
	Arnd Bergmann, Usyskin, Alexander, Avri Altman


> "Winkler, Tomas" <tomas.winkler@intel.com> writes:
> 
> >> The user space API is achieved via a number of synchronous IOCTLs.
> >>
> >>   * RPMB_IOC_VER_CMD - simple versioning API
> >>   * RPMB_IOC_CAP_CMD - query of underlying capabilities
> >>   * RPMB_IOC_PKEY_CMD - one time programming of access key
> >>   * RPMB_IOC_COUNTER_CMD - query the write counter
> >>   * RPMB_IOC_WBLOCKS_CMD - write blocks to device
> >>   * RPMB_IOC_RBLOCKS_CMD - read blocks from device
> >>
> >> The keys used for programming and writing blocks to the device are
> >> key_serial_t handles as provided by the keyctl() interface.
> >>
> >> [AJB: here there are two key differences between this and the
> >> original proposal. The first is the dropping of the sequence of
> >> preformated frames in favour of explicit actions. The second is the
> >> introduction of key_serial_t and the keyring API for referencing the
> >> key to use]
> >
> > Putting it gently I'm not sure this is good idea, from the security point of
> view.
> > The key has to be possession of the one that signs the frames as they are,
> it doesn't mean it is linux kernel keyring, it can be other party on different
> system.
> > With this approach you will make the other usecases not applicable. It
> > is less then trivial to move key securely from one system to another.
> 
> OK I can understand the desire for such a use-case but it does constrain the
> interface on the kernel with access to the hardware to purely providing a
> pipe to the raw hardware while also having to expose the details of the HW
> to userspace. 
This is the use case in Android. The key is in the "trusty" which different os running in a 	 virtual environment. The file storage abstraction is implemented there.
I'm not sure the point of constraining the kernel, can you please elaborate on that.

Also doesn't this break down after a PROGRAM_KEY event as
> the key will have had to traverse into the "untrusted" kernel?

This is one in a life event of the card happening on the manufacturing floor, maybe even not performed on Linux.

> I wonder if virtio-rpmb may be of help here? You could wrap up up the front-
> end in the security domain that has the keys although I don't know how easy
> it would be for a backend to work with real hardware?

I'm open to see any proposal, not sure I can wrap may head about it right now. 

Anyway I was about to send the new round of my code,  but let's come to common ground first. 

Thanks
Tomas

> 
> > We all wished it can be abstracted more but the frames has to come
> already signed, and the key material is inside the frame.
> > Thanks
> > Tomas
> >
> >
> >>
> >> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> >> Cc: Ulf Hansson <ulf.hansson@linaro.org>
> >> Cc: Linus  Walleij <linus.walleij@linaro.org>
> >> Cc: Arnd Bergmann <arnd.bergmann@linaro.org>
> >> Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> >> Cc: Tomas Winkler <tomas.winkler@intel.com>
> >> Cc: Alexander Usyskin <alexander.usyskin@intel.com>
> >> Cc: Avri Altman <avri.altman@sandisk.com>
> >> ---
> >>  .../userspace-api/ioctl/ioctl-number.rst      |   1 +
> >>  MAINTAINERS                                   |   1 +
> >>  drivers/char/rpmb/Kconfig                     |   7 +
> >>  drivers/char/rpmb/Makefile                    |   1 +
> >>  drivers/char/rpmb/cdev.c                      | 246 ++++++++++++++++++
> >>  drivers/char/rpmb/core.c                      |  10 +-
> >>  drivers/char/rpmb/rpmb-cdev.h                 |  17 ++
> >>  include/linux/rpmb.h                          |  10 +
> >>  include/uapi/linux/rpmb.h                     |  68 +++++
> >>  9 files changed, 357 insertions(+), 4 deletions(-)  create mode
> >> 100644 drivers/char/rpmb/cdev.c  create mode 100644
> >> drivers/char/rpmb/rpmb- cdev.h  create mode 100644
> >> include/uapi/linux/rpmb.h
> >>
> >> diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst
> >> b/Documentation/userspace-api/ioctl/ioctl-number.rst
> >> index a4c75a28c839..0ff2d4d81bb0 100644
> >> --- a/Documentation/userspace-api/ioctl/ioctl-number.rst
> >> +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
> >> @@ -344,6 +344,7 @@ Code  Seq#    Include File
> >> Comments
> >>  0xB5  00-0F  uapi/linux/rpmsg.h                                      <mailto:linux-
> >> remoteproc@vger.kernel.org>
> >>  0xB6  all    linux/fpga-dfl.h
> >>  0xB7  all    uapi/linux/remoteproc_cdev.h                            <mailto:linux-
> >> remoteproc@vger.kernel.org>
> >> +0xB8  80-8F  uapi/linux/rpmb.h                                       <mailto:linux-
> >> mmc@vger.kernel.org>
> >>  0xC0  00-0F  linux/usb/iowarrior.h
> >>  0xCA  00-0F  uapi/misc/cxl.h
> >>  0xCA  10-2F  uapi/misc/ocxl.h
> >> diff --git a/MAINTAINERS b/MAINTAINERS index
> >> 076f3983526c..c60b41b6e6bd 100644
> >> --- a/MAINTAINERS
> >> +++ b/MAINTAINERS
> >> @@ -15374,6 +15374,7 @@ M:	?
> >>  L:	linux-kernel@vger.kernel.org
> >>  S:	Supported
> >>  F:	drivers/char/rpmb/*
> >> +F:	include/uapi/linux/rpmb.h
> >>  F:	include/linux/rpmb.h
> >>
> >>  RTL2830 MEDIA DRIVER
> >> diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig
> >> index 431c2823cf70..9068664a399a 100644
> >> --- a/drivers/char/rpmb/Kconfig
> >> +++ b/drivers/char/rpmb/Kconfig
> >> @@ -9,3 +9,10 @@ config RPMB
> >>  	  access RPMB partition.
> >>
> >>  	  If unsure, select N.
> >> +
> >> +config RPMB_INTF_DEV
> >> +	bool "RPMB character device interface /dev/rpmbN"
> >> +	depends on RPMB && KEYS
> >> +	help
> >> +	  Say yes here if you want to access RPMB from user space
> >> +	  via character device interface /dev/rpmb%d
> >> diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile
> >> index 24d4752a9a53..f54b3f30514b 100644
> >> --- a/drivers/char/rpmb/Makefile
> >> +++ b/drivers/char/rpmb/Makefile
> >> @@ -3,5 +3,6 @@
> >>
> >>  obj-$(CONFIG_RPMB) += rpmb.o
> >>  rpmb-objs += core.o
> >> +rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o
> >>
> >>  ccflags-y += -D__CHECK_ENDIAN__
> >> diff --git a/drivers/char/rpmb/cdev.c b/drivers/char/rpmb/cdev.c new
> >> file mode 100644 index 000000000000..55f66720fd03
> >> --- /dev/null
> >> +++ b/drivers/char/rpmb/cdev.c
> >> @@ -0,0 +1,246 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright(c) 2015 - 2019 Intel Corporation.
> >> + */
> >> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> >> +
> >> +#include <linux/fs.h>
> >> +#include <linux/uaccess.h>
> >> +#include <linux/compat.h>
> >> +#include <linux/slab.h>
> >> +#include <linux/capability.h>
> >> +
> >> +#include <linux/rpmb.h>
> >> +
> >> +#include "rpmb-cdev.h"
> >> +
> >> +static dev_t rpmb_devt;
> >> +#define RPMB_MAX_DEVS  MINORMASK
> >> +
> >> +#define RPMB_DEV_OPEN    0  /** single open bit (position) */
> >> +
> >> +/**
> >> + * rpmb_open - the open function
> >> + *
> >> + * @inode: pointer to inode structure
> >> + * @fp: pointer to file structure
> >> + *
> >> + * Return: 0 on success, <0 on error  */ static int rpmb_open(struct
> >> +inode *inode, struct file *fp) {
> >> +	struct rpmb_dev *rdev;
> >> +
> >> +	rdev = container_of(inode->i_cdev, struct rpmb_dev, cdev);
> >> +	if (!rdev)
> >> +		return -ENODEV;
> >> +
> >> +	/* the rpmb is single open! */
> >> +	if (test_and_set_bit(RPMB_DEV_OPEN, &rdev->status))
> >> +		return -EBUSY;
> >> +
> >> +	mutex_lock(&rdev->lock);
> >> +
> >> +	fp->private_data = rdev;
> >> +
> >> +	mutex_unlock(&rdev->lock);
> >> +
> >> +	return nonseekable_open(inode, fp); }
> >> +
> >> +/**
> >> + * rpmb_release - the cdev release function
> >> + *
> >> + * @inode: pointer to inode structure
> >> + * @fp: pointer to file structure
> >> + *
> >> + * Return: 0 always.
> >> + */
> >> +static int rpmb_release(struct inode *inode, struct file *fp) {
> >> +	struct rpmb_dev *rdev = fp->private_data;
> >> +
> >> +	clear_bit(RPMB_DEV_OPEN, &rdev->status);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static long rpmb_ioctl_ver_cmd(struct rpmb_dev *rdev,
> >> +			       struct rpmb_ioc_ver_cmd __user *ptr) {
> >> +	struct rpmb_ioc_ver_cmd ver = {
> >> +		.api_version = RPMB_API_VERSION,
> >> +	};
> >> +
> >> +	return copy_to_user(ptr, &ver, sizeof(ver)) ? -EFAULT : 0; }
> >> +
> >> +static long rpmb_ioctl_cap_cmd(struct rpmb_dev *rdev,
> >> +			       struct rpmb_ioc_cap_cmd __user *ptr) {
> >> +	struct rpmb_ioc_cap_cmd cap;
> >> +
> >> +	cap.target      = rdev->target;
> >> +	cap.block_size  = rdev->ops->block_size;
> >> +	cap.wr_cnt_max  = rdev->ops->wr_cnt_max;
> >> +	cap.rd_cnt_max  = rdev->ops->rd_cnt_max;
> >> +	cap.auth_method = rdev->ops->auth_method;
> >> +	cap.capacity    = rpmb_get_capacity(rdev);
> >> +	cap.reserved    = 0;
> >> +
> >> +	return copy_to_user(ptr, &cap, sizeof(cap)) ? -EFAULT : 0; }
> >> +
> >> +static long rpmb_ioctl_pkey_cmd(struct rpmb_dev *rdev, key_serial_t
> >> +__user *k) {
> >> +	key_serial_t keyid;
> >> +
> >> +	if (get_user(keyid, k))
> >> +		return -EFAULT;
> >> +	else
> >> +		return rpmb_program_key(rdev, keyid); }
> >> +
> >> +static long rpmb_ioctl_counter_cmd(struct rpmb_dev *rdev, int __user
> >> +*ptr) {
> >> +	int count = rpmb_get_write_count(rdev);
> >> +
> >> +	if (count > 0)
> >> +		return put_user(count, ptr);
> >> +	else
> >> +		return count;
> >> +}
> >> +
> >> +static long rpmb_ioctl_wblocks_cmd(struct rpmb_dev *rdev,
> >> +				   struct rpmb_ioc_blocks_cmd __user *ptr) {
> >> +	struct rpmb_ioc_blocks_cmd wblocks;
> >> +	int sz;
> >> +	long ret;
> >> +	u8 *data;
> >> +
> >> +	if (copy_from_user(&wblocks, ptr, sizeof(struct
> >> rpmb_ioc_blocks_cmd)))
> >> +		return -EFAULT;
> >> +
> >> +	/* Don't write more blocks device supports */
> >> +	if (wblocks.count > rdev->ops->wr_cnt_max)
> >> +		return -EINVAL;
> >> +
> >> +	sz = wblocks.count * 256;
> >> +	data = kmalloc(sz, GFP_KERNEL);
> >> +
> >> +	if (!data)
> >> +		return -ENOMEM;
> >> +
> >> +	if (copy_from_user(data, wblocks.data, sz))
> >> +		ret = -EFAULT;
> >> +	else
> >> +		ret = rpmb_write_blocks(rdev, wblocks.key, wblocks.addr,
> >> +wblocks.count, data);
> >> +
> >> +	kfree(data);
> >> +	return ret;
> >> +}
> >> +
> >> +static long rpmb_ioctl_rblocks_cmd(struct rpmb_dev *rdev,
> >> +				   struct rpmb_ioc_blocks_cmd __user *ptr) {
> >> +	struct rpmb_ioc_blocks_cmd rblocks;
> >> +	int sz;
> >> +	long ret;
> >> +	u8 *data;
> >> +
> >> +	if (copy_from_user(&rblocks, ptr, sizeof(struct
> >> rpmb_ioc_blocks_cmd)))
> >> +		return -EFAULT;
> >> +
> >> +	if (rblocks.count > rdev->ops->rd_cnt_max)
> >> +		return -EINVAL;
> >> +
> >> +	sz = rblocks.count * 256;
> >> +	data = kmalloc(sz, GFP_KERNEL);
> >> +
> >> +	if (!data)
> >> +		return -ENOMEM;
> >> +
> >> +	ret = rpmb_read_blocks(rdev, rblocks.addr, rblocks.count, data);
> >> +
> >> +	if (ret == 0)
> >> +		ret = copy_to_user(rblocks.data, data, sz);
> >> +
> >> +	kfree(data);
> >> +	return ret;
> >> +}
> >> +
> >> +/**
> >> + * rpmb_ioctl - rpmb ioctl dispatcher
> >> + *
> >> + * @fp: a file pointer
> >> + * @cmd: ioctl command RPMB_IOC_SEQ_CMD RPMB_IOC_VER_CMD
> >> +RPMB_IOC_CAP_CMD
> >> + * @arg: ioctl data: rpmb_ioc_ver_cmd rpmb_ioc_cap_cmd
> >> pmb_ioc_seq_cmd
> >> + *
> >> + * Return: 0 on success; < 0 on error  */ static long
> >> +rpmb_ioctl(struct file *fp, unsigned int cmd, unsigned long
> >> +arg) {
> >> +	struct rpmb_dev *rdev = fp->private_data;
> >> +	void __user *ptr = (void __user *)arg;
> >> +
> >> +	switch (cmd) {
> >> +	case RPMB_IOC_VER_CMD:
> >> +		return rpmb_ioctl_ver_cmd(rdev, ptr);
> >> +	case RPMB_IOC_CAP_CMD:
> >> +		return rpmb_ioctl_cap_cmd(rdev, ptr);
> >> +	case RPMB_IOC_PKEY_CMD:
> >> +		return rpmb_ioctl_pkey_cmd(rdev, ptr);
> >> +	case RPMB_IOC_COUNTER_CMD:
> >> +		return rpmb_ioctl_counter_cmd(rdev, ptr);
> >> +	case RPMB_IOC_WBLOCKS_CMD:
> >> +		return rpmb_ioctl_wblocks_cmd(rdev, ptr);
> >> +	case RPMB_IOC_RBLOCKS_CMD:
> >> +		return rpmb_ioctl_rblocks_cmd(rdev, ptr);
> >> +	default:
> >> +		dev_err(&rdev->dev, "unsupported ioctl 0x%x.\n", cmd);
> >> +		return -ENOIOCTLCMD;
> >> +	}
> >> +}
> >> +
> >> +static const struct file_operations rpmb_fops = {
> >> +	.open           = rpmb_open,
> >> +	.release        = rpmb_release,
> >> +	.unlocked_ioctl = rpmb_ioctl,
> >> +	.owner          = THIS_MODULE,
> >> +	.llseek         = noop_llseek,
> >> +};
> >> +
> >> +void rpmb_cdev_prepare(struct rpmb_dev *rdev) {
> >> +	rdev->dev.devt = MKDEV(MAJOR(rpmb_devt), rdev->id);
> >> +	rdev->cdev.owner = THIS_MODULE;
> >> +	cdev_init(&rdev->cdev, &rpmb_fops); }
> >> +
> >> +void rpmb_cdev_add(struct rpmb_dev *rdev) {
> >> +	cdev_add(&rdev->cdev, rdev->dev.devt, 1); }
> >> +
> >> +void rpmb_cdev_del(struct rpmb_dev *rdev) {
> >> +	if (rdev->dev.devt)
> >> +		cdev_del(&rdev->cdev);
> >> +}
> >> +
> >> +int __init rpmb_cdev_init(void)
> >> +{
> >> +	int ret;
> >> +
> >> +	ret = alloc_chrdev_region(&rpmb_devt, 0, RPMB_MAX_DEVS,
> >> "rpmb");
> >> +	if (ret < 0)
> >> +		pr_err("unable to allocate char dev region\n");
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +void __exit rpmb_cdev_exit(void)
> >> +{
> >> +	unregister_chrdev_region(rpmb_devt, RPMB_MAX_DEVS); }
> >> diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c
> >> index
> >> a2e21c14986a..e26d605e48e1 100644
> >> --- a/drivers/char/rpmb/core.c
> >> +++ b/drivers/char/rpmb/core.c
> >> @@ -12,6 +12,7 @@
> >>  #include <linux/slab.h>
> >>
> >>  #include <linux/rpmb.h>
> >> +#include "rpmb-cdev.h"
> >>
> >>  static DEFINE_IDA(rpmb_ida);
> >>
> >> @@ -277,6 +278,7 @@ int rpmb_dev_unregister(struct rpmb_dev *rdev)
> >>  		return -EINVAL;
> >>
> >>  	mutex_lock(&rdev->lock);
> >> +	rpmb_cdev_del(rdev);
> >>  	device_del(&rdev->dev);
> >>  	mutex_unlock(&rdev->lock);
> >>
> >> @@ -371,9 +373,6 @@ struct rpmb_dev *rpmb_dev_register(struct
> device
> >> *dev, u8 target,
> >>  	if (!ops->read_blocks)
> >>  		return ERR_PTR(-EINVAL);
> >>
> >> -	if (ops->type == RPMB_TYPE_ANY || ops->type >
> >> RPMB_TYPE_MAX)
> >> -		return ERR_PTR(-EINVAL);
> >> -
> >>  	rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
> >>  	if (!rdev)
> >>  		return ERR_PTR(-ENOMEM);
> >> @@ -396,6 +395,8 @@ struct rpmb_dev *rpmb_dev_register(struct
> device
> >> *dev, u8 target,
> >>  	if (ret)
> >>  		goto exit;
> >>
> >> +	rpmb_cdev_add(rdev);
> >> +
> >>  	dev_dbg(&rdev->dev, "registered device\n");
> >>
> >>  	return rdev;
> >> @@ -412,11 +413,12 @@ static int __init rpmb_init(void)  {
> >>  	ida_init(&rpmb_ida);
> >>  	class_register(&rpmb_class);
> >> -	return 0;
> >> +	return rpmb_cdev_init();
> >>  }
> >>
> >>  static void __exit rpmb_exit(void)
> >>  {
> >> +	rpmb_cdev_exit();
> >>  	class_unregister(&rpmb_class);
> >>  	ida_destroy(&rpmb_ida);
> >>  }
> >> diff --git a/drivers/char/rpmb/rpmb-cdev.h b/drivers/char/rpmb/rpmb-
> >> cdev.h new file mode 100644 index 000000000000..e59ff0c05e9d
> >> --- /dev/null
> >> +++ b/drivers/char/rpmb/rpmb-cdev.h
> >> @@ -0,0 +1,17 @@
> >> +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2015-2018 Intel Corp. All rights reserved  */
> >> +#ifdef CONFIG_RPMB_INTF_DEV int __init rpmb_cdev_init(void); void
> >> +__exit rpmb_cdev_exit(void); void rpmb_cdev_prepare(struct
> rpmb_dev
> >> +*rdev); void rpmb_cdev_add(struct rpmb_dev *rdev); void
> >> +rpmb_cdev_del(struct rpmb_dev *rdev); #else static inline int __init
> >> +rpmb_cdev_init(void) { return 0; } static inline void __exit
> >> +rpmb_cdev_exit(void) {} static inline void rpmb_cdev_prepare(struct
> >> +rpmb_dev *rdev) {} static inline void rpmb_cdev_add(struct rpmb_dev
> >> +*rdev) {} static inline void rpmb_cdev_del(struct rpmb_dev *rdev) {}
> >> +#endif /*
> >> CONFIG_RPMB_INTF_DEV
> >> +*/
> >> diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h index
> >> 718ba7c91ecd..fe44f60efe31 100644
> >> --- a/include/linux/rpmb.h
> >> +++ b/include/linux/rpmb.h
> >> @@ -8,9 +8,13 @@
> >>
> >>  #include <linux/types.h>
> >>  #include <linux/device.h>
> >> +#include <linux/cdev.h>
> >> +#include <uapi/linux/rpmb.h>
> >>  #include <linux/kref.h>
> >>  #include <linux/key.h>
> >>
> >> +#define RPMB_API_VERSION 0x80000001
> >> +
> >>  /**
> >>   * struct rpmb_ops - RPMB ops to be implemented by underlying block
> >> device
> >>   *
> >> @@ -51,6 +55,8 @@ struct rpmb_ops {
> >>   * @dev        : device
> >>   * @id         : device id
> >>   * @target     : RPMB target/region within the physical device
> >> + * @cdev       : character dev
> >> + * @status     : device status
> >>   * @ops        : operation exported by block layer
> >>   */
> >>  struct rpmb_dev {
> >> @@ -58,6 +64,10 @@ struct rpmb_dev {
> >>  	struct device dev;
> >>  	int id;
> >>  	u8 target;
> >> +#ifdef CONFIG_RPMB_INTF_DEV
> >> +	struct cdev cdev;
> >> +	unsigned long status;
> >> +#endif /* CONFIG_RPMB_INTF_DEV */
> >>  	const struct rpmb_ops *ops;
> >>  };
> >>
> >> diff --git a/include/uapi/linux/rpmb.h b/include/uapi/linux/rpmb.h
> >> new file mode 100644 index 000000000000..3957b785cdd5
> >> --- /dev/null
> >> +++ b/include/uapi/linux/rpmb.h
> >> @@ -0,0 +1,68 @@
> >> +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR
> >> +BSD-3-Clause) */
> >> +/*
> >> + * Copyright (C) 2015-2018 Intel Corp. All rights reserved
> >> + * Copyright (C) 2021 Linaro Ltd
> >> + */
> >> +#ifndef _UAPI_LINUX_RPMB_H_
> >> +#define _UAPI_LINUX_RPMB_H_
> >> +
> >> +#include <linux/types.h>
> >> +
> >> +/**
> >> + * struct rpmb_ioc_ver_cmd - rpmb api version
> >> + *
> >> + * @api_version: rpmb API version.
> >> + */
> >> +struct rpmb_ioc_ver_cmd {
> >> +	__u32 api_version;
> >> +} __packed;
> >> +
> >> +enum rpmb_auth_method {
> >> +	RPMB_HMAC_ALGO_SHA_256 = 0,
> >> +};
> >> +
> >> +/**
> >> + * struct rpmb_ioc_cap_cmd - rpmb capabilities
> >> + *
> >> + * @target: rpmb target/region within RPMB partition.
> >> + * @capacity: storage capacity (in units of 128K)
> >> + * @block_size: storage data block size (in units of 256B)
> >> + * @wr_cnt_max: maximal number of block that can be written in a
> >> +single
> >> request.
> >> + * @rd_cnt_max: maximal number of block that can be read in a single
> >> request.
> >> + * @auth_method: authentication method: currently always
> >> HMAC_SHA_256
> >> + * @reserved: reserved to align to 4 bytes.
> >> + */
> >> +struct rpmb_ioc_cap_cmd {
> >> +	__u16 target;
> >> +	__u16 capacity;
> >> +	__u16 block_size;
> >> +	__u16 wr_cnt_max;
> >> +	__u16 rd_cnt_max;
> >> +	__u16 auth_method;
> >> +	__u16 reserved;
> >> +} __attribute__((packed));
> >> +
> >> +/**
> >> + * struct rpmb_ioc_blocks_cmd - read/write blocks to/from RPMB
> >> + *
> >> + * @keyid: key_serial_t of key to use
> >> + * @addr: index into device (units of 256B blocks)
> >> + * @count: number of 256B blocks
> >> + * @data: pointer to data to write/read  */ struct
> >> +rpmb_ioc_blocks_cmd {
> >> +	__s32 key; /* key_serial_t */
> >> +	__u32 addr;
> >> +	__u32 count;
> >> +	__u8 __user *data;
> >> +} __attribute__((packed));
> >> +
> >> +
> >> +#define RPMB_IOC_VER_CMD     _IOR(0xB8, 80, struct
> rpmb_ioc_ver_cmd)
> >> +#define RPMB_IOC_CAP_CMD     _IOR(0xB8, 81, struct
> rpmb_ioc_cap_cmd)
> >> +#define RPMB_IOC_PKEY_CMD    _IOW(0xB8, 82, key_serial_t)
> >> +#define RPMB_IOC_COUNTER_CMD _IOR(0xB8, 84, int) #define
> >> +RPMB_IOC_WBLOCKS_CMD _IOW(0xB8, 85, struct
> rpmb_ioc_blocks_cmd)
> >> #define
> >> +RPMB_IOC_RBLOCKS_CMD _IOR(0xB8, 86, struct
> rpmb_ioc_blocks_cmd)
> >> +
> >> +#endif /* _UAPI_LINUX_RPMB_H_ */
> >> --
> >> 2.20.1
> >
> 
> --
> Alex Bennée

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

* Re: [RFC PATCH  2/5] char: rpmb: provide a user space interface
  2021-03-04 10:34       ` Winkler, Tomas
@ 2021-03-04 17:52         ` Alex Bennée
  2021-03-04 19:54           ` Winkler, Tomas
  0 siblings, 1 reply; 47+ messages in thread
From: Alex Bennée @ 2021-03-04 17:52 UTC (permalink / raw)
  To: Winkler, Tomas
  Cc: linux-kernel, maxim.uvarov, joakim.bech, ilias.apalodimas, arnd,
	ruchika.gupta, Huang, Yang, Zhu, Bing, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Linus Walleij,
	Arnd Bergmann, Usyskin, Alexander, Avri Altman


Winkler, Tomas <tomas.winkler@intel.com> writes:

>> "Winkler, Tomas" <tomas.winkler@intel.com> writes:
>> 
>> >> The user space API is achieved via a number of synchronous IOCTLs.
>> >>
>> >>   * RPMB_IOC_VER_CMD - simple versioning API
>> >>   * RPMB_IOC_CAP_CMD - query of underlying capabilities
>> >>   * RPMB_IOC_PKEY_CMD - one time programming of access key
>> >>   * RPMB_IOC_COUNTER_CMD - query the write counter
>> >>   * RPMB_IOC_WBLOCKS_CMD - write blocks to device
>> >>   * RPMB_IOC_RBLOCKS_CMD - read blocks from device
>> >>
>> >> The keys used for programming and writing blocks to the device are
>> >> key_serial_t handles as provided by the keyctl() interface.
>> >>
>> >> [AJB: here there are two key differences between this and the
>> >> original proposal. The first is the dropping of the sequence of
>> >> preformated frames in favour of explicit actions. The second is the
>> >> introduction of key_serial_t and the keyring API for referencing the
>> >> key to use]
>> >
>> > Putting it gently I'm not sure this is good idea, from the security point of
>> view.
>> > The key has to be possession of the one that signs the frames as they are,
>> it doesn't mean it is linux kernel keyring, it can be other party on different
>> system.
>> > With this approach you will make the other usecases not applicable. It
>> > is less then trivial to move key securely from one system to another.
>> 
>> OK I can understand the desire for such a use-case but it does constrain the
>> interface on the kernel with access to the hardware to purely providing a
>> pipe to the raw hardware while also having to expose the details of the HW
>> to userspace. 
> This is the use case in Android. The key is in the "trusty" which
> different os running in a virtual environment. The file storage
> abstraction is implemented there. I'm not sure the point of
> constraining the kernel, can you please elaborate on that.

Well the kernel is all about abstracting differences not baking in
assumptions. However can I ask a bit more about this security model?

Is the secure enclave just a separate userspace process or is it in a
separate virtual machine? Is it accessible at all by the kernel running
the driver?

The fact that key id is passed down into the kernel doesn't have to
imply the kernel does the final cryptographic operation. In the ARM
world you could make a call to the secure world to do the operation for
you. I note the keyctl() interface already has support for going to
userspace to make queries of the keyring. Maybe what is really needed is
an abstraction for the kernel to delegate the MAC calculation to some other
trusted process that also understands the keyid.

>
> Also doesn't this break down after a PROGRAM_KEY event as
>> the key will have had to traverse into the "untrusted" kernel?
>
> This is one in a life event of the card happening on the manufacturing
> floor, maybe even not performed on Linux.

In an off list conversation it was suggested that maybe the PROGRAM_KEY
ioctl should be disabled for locked down kernels to dissuade production
use of the facility (it is handy for testing though!).

>> I wonder if virtio-rpmb may be of help here? You could wrap up up the front-
>> end in the security domain that has the keys although I don't know how easy
>> it would be for a backend to work with real hardware?
>
> I'm open to see any proposal, not sure I can wrap may head about it right now. 
>
> Anyway I was about to send the new round of my code,  but let's come to common ground first. 
>

OK - I'll see what the others say.

-- 
Alex Bennée

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

* RE: [RFC PATCH  2/5] char: rpmb: provide a user space interface
  2021-03-04 17:52         ` Alex Bennée
@ 2021-03-04 19:54           ` Winkler, Tomas
  2021-03-04 21:43             ` Arnd Bergmann
  0 siblings, 1 reply; 47+ messages in thread
From: Winkler, Tomas @ 2021-03-04 19:54 UTC (permalink / raw)
  To: Alex Bennée
  Cc: linux-kernel, maxim.uvarov, joakim.bech, ilias.apalodimas, arnd,
	ruchika.gupta, Huang, Yang, Zhu, Bing, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Linus Walleij,
	Arnd Bergmann, Usyskin, Alexander, Avri Altman



> 
> Winkler, Tomas <tomas.winkler@intel.com> writes:
> 
> >> "Winkler, Tomas" <tomas.winkler@intel.com> writes:
> >>
> >> >> The user space API is achieved via a number of synchronous IOCTLs.
> >> >>
> >> >>   * RPMB_IOC_VER_CMD - simple versioning API
> >> >>   * RPMB_IOC_CAP_CMD - query of underlying capabilities
> >> >>   * RPMB_IOC_PKEY_CMD - one time programming of access key
> >> >>   * RPMB_IOC_COUNTER_CMD - query the write counter
> >> >>   * RPMB_IOC_WBLOCKS_CMD - write blocks to device
> >> >>   * RPMB_IOC_RBLOCKS_CMD - read blocks from device
> >> >>
> >> >> The keys used for programming and writing blocks to the device are
> >> >> key_serial_t handles as provided by the keyctl() interface.
> >> >>
> >> >> [AJB: here there are two key differences between this and the
> >> >> original proposal. The first is the dropping of the sequence of
> >> >> preformated frames in favour of explicit actions. The second is
> >> >> the introduction of key_serial_t and the keyring API for
> >> >> referencing the key to use]
> >> >
> >> > Putting it gently I'm not sure this is good idea, from the security
> >> > point of
> >> view.
> >> > The key has to be possession of the one that signs the frames as
> >> > they are,
> >> it doesn't mean it is linux kernel keyring, it can be other party on
> >> different system.
> >> > With this approach you will make the other usecases not applicable.
> >> > It is less then trivial to move key securely from one system to another.
> >>
> >> OK I can understand the desire for such a use-case but it does
> >> constrain the interface on the kernel with access to the hardware to
> >> purely providing a pipe to the raw hardware while also having to
> >> expose the details of the HW to userspace.
> > This is the use case in Android. The key is in the "trusty" which
> > different os running in a virtual environment. The file storage
> > abstraction is implemented there. I'm not sure the point of
> > constraining the kernel, can you please elaborate on that.
> 
> Well the kernel is all about abstracting differences not baking in assumptions.
> However can I ask a bit more about this security model?
> Is the secure enclave just a separate userspace process or is it in a separate
> virtual machine? Is it accessible at all by the kernel running the driver?

It's not an assumption this is working for few years already (https://source.android.com/security/trusty#application_services) 
The model is that you have a trusted environment (TEE)  in which can be in any of the form you described above.
And there is established agreement via the RPMB key that TEE is only entity that can produce content to be stored on RPBM,
The RPMB hardware also ensure that nobody can catch it in the middle and replay that storage event. 

My point is that interface you are suggesting is not covering all possible usages of RPMB, actually usages that are already in place.

> The fact that key id is passed down into the kernel doesn't have to imply the
> kernel does the final cryptographic operation. In the ARM world you could
> make a call to the secure world to do the operation for you. I note the
> keyctl() interface already has support for going to userspace to make queries
> of the keyring.  Maybe what is really needed is an abstraction for the kernel
> to delegate the MAC calculation to some other trusted process that also
> understands the keyid.

Sure but that you want need to make sure that the entity that creates the content has the right to use this specific key, so you will need to create another channel of trust. 
And this trust has to be established somewhere at the manufacturing time. 

> >
> > Also doesn't this break down after a PROGRAM_KEY event as
> >> the key will have had to traverse into the "untrusted" kernel?
> >
> > This is one in a life event of the card happening on the manufacturing
> > floor, maybe even not performed on Linux.
> 
> In an off list conversation it was suggested that maybe the PROGRAM_KEY
> ioctl should be disabled for locked down kernels to dissuade production use
> of the facility (it is handy for testing t

This is really protected by the hardware,  also once you are programming key your platform would be rather sealed already at least it's TEE environment, as this is the other part that knows the key.

> >> I wonder if virtio-rpmb may be of help here? You could wrap up up the
> >> front- end in the security domain that has the keys although I don't
> >> know how easy it would be for a backend to work with real hardware?
> >
> > I'm open to see any proposal, not sure I can wrap may head about it right
> now.
> >
> > Anyway I was about to send the new round of my code,  but let's come to
> common ground first.
> >
> 
> OK - I'll see what the others say.
> 
> --
> Alex Bennée

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-03 13:54 ` [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem Alex Bennée
  2021-03-03 15:28   ` Ulf Hansson
@ 2021-03-04 20:56   ` Arnd Bergmann
  2021-03-05  7:51     ` Joakim Bech
  1 sibling, 1 reply; 47+ messages in thread
From: Arnd Bergmann @ 2021-03-04 20:56 UTC (permalink / raw)
  To: Alex Bennée
  Cc: linux-kernel, Maxim Uvarov, Joakim Bech, Ilias Apalodimas,
	ruchika.gupta, Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell,
	hmo, linux-mmc, linux-scsi, linux-nvme, Ulf Hansson,
	Linus Walleij, Arnd Bergmann

On Wed, Mar 3, 2021 at 2:54 PM Alex Bennée <alex.bennee@linaro.org> wrote:
>
> A number of storage technologies support a specialised hardware
> partition designed to be resistant to replay attacks. The underlying
> HW protocols differ but the operations are common. The RPMB partition
> cannot be accessed via standard block layer, but by a set of specific
> commands: WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY. Such a
> partition provides authenticated and replay protected access, hence
> suitable as a secure storage.
>
> The RPMB layer aims to provide in-kernel API for Trusted Execution
> Environment (TEE) devices that are capable to securely compute block
> frame signature. In case a TEE device wishes to store a replay
> protected data, requests the storage device via RPMB layer to store
> the data.
>
> A TEE device driver can claim the RPMB interface, for example, via
> class_interface_register(). The RPMB layer provides a series of
> operations for interacting with the device.
>
>   * program_key - a one time operation for setting up a new device
>   * get_capacity - introspect the device capacity
>   * get_write_count - check the write counter
>   * write_blocks - write a series of blocks to the RPMB device
>   * read_blocks - read a series of blocks from the RPMB device

Based on the discussion we had today in a meeting, it seems the
main change that is needed is to get back to the original model
of passing the encrypted data to the kernel instead of cleartext
data, as the main use case we know of is to have the key inside of
the TEE device and not available to the kernel or user space.

This is also required to be able to forward the encrypted data
through the same interface on a KVM host, when the guest
uses virtio-rpmb, and the host forwards the data into an mmc or
ufs device.

That said, I can also imagine use cases where we do want to
store the key in the kernel's keyring, so maybe we end up needing
both.

> The detailed operation of implementing the access is left to the TEE
> device driver itself.
>
> [This is based-on Thomas Winkler's proposed API from:
>
>   https://lore.kernel.org/linux-mmc/1478548394-8184-2-git-send-email-tomas.winkler@intel.com/
>
> The principle difference is the framing details and HW specific
> bits (JDEC vs NVME frames) are left to the lower level TEE driver to
> worry about. The eventual userspace ioctl interface will aim to be
> similarly generic. This is an RFC to follow up on:
>
>   Subject: RPMB user space ABI
>   Date: Thu, 11 Feb 2021 14:07:00 +0000
>   Message-ID: <87mtwashi4.fsf@linaro.org>]
>
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> Cc: Tomas Winkler <tomas.winkler@intel.com>
> Cc: Ulf Hansson <ulf.hansson@linaro.org>
> Cc: Linus  Walleij <linus.walleij@linaro.org>
> Cc: Arnd Bergmann <arnd.bergmann@linaro.org>
> Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> ---
>  MAINTAINERS                |   7 +
>  drivers/char/Kconfig       |   2 +
>  drivers/char/Makefile      |   1 +
>  drivers/char/rpmb/Kconfig  |  11 +
>  drivers/char/rpmb/Makefile |   7 +
>  drivers/char/rpmb/core.c   | 429 +++++++++++++++++++++++++++++++++++++
>  include/linux/rpmb.h       | 163 ++++++++++++++


My feeling is that it should be a top-level subsystem, in drivers/rpmb
rather than drivers/char/rpmb, as you implement an abstraction layer
that other drivers can plug into, rather than a simple driver.

       Arnd

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

* Re: [RFC PATCH 2/5] char: rpmb: provide a user space interface
  2021-03-03 13:54 ` [RFC PATCH 2/5] char: rpmb: provide a user space interface Alex Bennée
  2021-03-04  7:01   ` Winkler, Tomas
@ 2021-03-04 21:08   ` Arnd Bergmann
  1 sibling, 0 replies; 47+ messages in thread
From: Arnd Bergmann @ 2021-03-04 21:08 UTC (permalink / raw)
  To: Alex Bennée
  Cc: linux-kernel, Maxim Uvarov, Joakim Bech, Ilias Apalodimas,
	ruchika.gupta, Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell,
	hmo, linux-mmc, linux-scsi, linux-nvme, Ulf Hansson,
	Linus Walleij, Arnd Bergmann, Alexander Usyskin, Avri Altman

On Wed, Mar 3, 2021 at 2:54 PM Alex Bennée <alex.bennee@linaro.org> wrote:
>
> +       /* the rpmb is single open! */
> +       if (test_and_set_bit(RPMB_DEV_OPEN, &rdev->status))
> +               return -EBUSY;

open counters on device nodes are fundamentally broken, because
they do not stop you from using dup() or sharing the file descriptor
across a fork. Just remove this.

> +static long rpmb_ioctl_ver_cmd(struct rpmb_dev *rdev,
> +                              struct rpmb_ioc_ver_cmd __user *ptr)
> +{
> +       struct rpmb_ioc_ver_cmd ver = {
> +               .api_version = RPMB_API_VERSION,
> +       };
> +
> +       return copy_to_user(ptr, &ver, sizeof(ver)) ? -EFAULT : 0;
> +}

Similarly, API versions are fundamentally flawed, as the kernel requires
us to keep compatibility with existing user space. Remove this as well.

> +static long rpmb_ioctl_cap_cmd(struct rpmb_dev *rdev,
> +                              struct rpmb_ioc_cap_cmd __user *ptr)
> +{
> +       struct rpmb_ioc_cap_cmd cap;

Better do a memset() here to ensure this does not leak kernel
stack data to user space.


> +static const struct file_operations rpmb_fops = {
> +       .open           = rpmb_open,
> +       .release        = rpmb_release,
> +       .unlocked_ioctl = rpmb_ioctl,
> +       .owner          = THIS_MODULE,
> +       .llseek         = noop_llseek,
> +};

Add

       .compat_ioctl = compat_ptr_ioctl

to make it work for 32-bit user space on 64-bit kernels.

> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
> +/*
> + * Copyright (C) 2015-2018 Intel Corp. All rights reserved
> + */
> +#ifdef CONFIG_RPMB_INTF_DEV
> +int __init rpmb_cdev_init(void);
> +void __exit rpmb_cdev_exit(void);
> +void rpmb_cdev_prepare(struct rpmb_dev *rdev);
> +void rpmb_cdev_add(struct rpmb_dev *rdev);
> +void rpmb_cdev_del(struct rpmb_dev *rdev);
> +#else
> +static inline int __init rpmb_cdev_init(void) { return 0; }

I don't think it's necessary to make the user interface optional,
I'd just always provide these.

>
> +#define RPMB_API_VERSION 0x80000001

Remove this

> + */
> +struct rpmb_ioc_ver_cmd {
> +       __u32 api_version;
> +} __packed;

And this

> +
> +enum rpmb_auth_method {
> +       RPMB_HMAC_ALGO_SHA_256 = 0,
> +};
> +
> +/**
> + * struct rpmb_ioc_cap_cmd - rpmb capabilities
> + *
> + * @target: rpmb target/region within RPMB partition.
> + * @capacity: storage capacity (in units of 128K)
> + * @block_size: storage data block size (in units of 256B)
> + * @wr_cnt_max: maximal number of block that can be written in a single request.
> + * @rd_cnt_max: maximal number of block that can be read in a single request.
> + * @auth_method: authentication method: currently always HMAC_SHA_256
> + * @reserved: reserved to align to 4 bytes.
> + */
> +struct rpmb_ioc_cap_cmd {
> +       __u16 target;
> +       __u16 capacity;
> +       __u16 block_size;
> +       __u16 wr_cnt_max;
> +       __u16 rd_cnt_max;
> +       __u16 auth_method;
> +       __u16 reserved;
> +} __attribute__((packed));
> +

Remove the packed attribute, it does not change the structure layout but
just makes it less efficient to access on architectures that turn unaligned
loads and stores into byte accesses.

> +/**
> + * struct rpmb_ioc_blocks_cmd - read/write blocks to/from RPMB
> + *
> + * @keyid: key_serial_t of key to use
> + * @addr: index into device (units of 256B blocks)
> + * @count: number of 256B blocks
> + * @data: pointer to data to write/read
> + */
> +struct rpmb_ioc_blocks_cmd {
> +       __s32 key; /* key_serial_t */
> +       __u32 addr;
> +       __u32 count;
> +       __u8 __user *data;
> +} __attribute__((packed));

ioctl structures should generally not have pointers in them. If this can be done
one block at a time, you can have the 256 bytes as part of the structure.

This probably needs a redesign anyway based on Tomas' feedback though.

If you end up needing a pointer, use a __u64 member  with
u64_to_user_ptr() as described in Documentation/driver-api/ioctl.rst

> +#define RPMB_IOC_VER_CMD     _IOR(0xB8, 80, struct rpmb_ioc_ver_cmd)
> +#define RPMB_IOC_CAP_CMD     _IOR(0xB8, 81, struct rpmb_ioc_cap_cmd)
> +#define RPMB_IOC_PKEY_CMD    _IOW(0xB8, 82, key_serial_t)
> +#define RPMB_IOC_COUNTER_CMD _IOR(0xB8, 84, int)
> +#define RPMB_IOC_WBLOCKS_CMD _IOW(0xB8, 85, struct rpmb_ioc_blocks_cmd)
> +#define RPMB_IOC_RBLOCKS_CMD _IOR(0xB8, 86, struct rpmb_ioc_blocks_cmd)

The last one should be _IOWR(), not _IOR(), since you write the metadata and
read the data.

      Arnd

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

* Re: [RFC PATCH 2/5] char: rpmb: provide a user space interface
  2021-03-04 19:54           ` Winkler, Tomas
@ 2021-03-04 21:43             ` Arnd Bergmann
  2021-03-05  6:31               ` Winkler, Tomas
  0 siblings, 1 reply; 47+ messages in thread
From: Arnd Bergmann @ 2021-03-04 21:43 UTC (permalink / raw)
  To: Winkler, Tomas
  Cc: Alex Bennée, linux-kernel, maxim.uvarov, joakim.bech,
	ilias.apalodimas, ruchika.gupta, Huang, Yang, Zhu, Bing,
	Matti.Moell, hmo, linux-mmc, linux-scsi, linux-nvme, Ulf Hansson,
	Linus Walleij, Arnd Bergmann, Usyskin, Alexander, Avri Altman

On Thu, Mar 4, 2021 at 8:54 PM Winkler, Tomas <tomas.winkler@intel.com> wrote:
> > Winkler, Tomas <tomas.winkler@intel.com> writes:
> > >> "Winkler, Tomas" <tomas.winkler@intel.com> writes:
> > >>
> > >> >> The user space API is achieved via a number of synchronous IOCTLs.
> > >> >>
> > >> >>   * RPMB_IOC_VER_CMD - simple versioning API
> > >> >>   * RPMB_IOC_CAP_CMD - query of underlying capabilities
> > >> >>   * RPMB_IOC_PKEY_CMD - one time programming of access key
> > >> >>   * RPMB_IOC_COUNTER_CMD - query the write counter
> > >> >>   * RPMB_IOC_WBLOCKS_CMD - write blocks to device
> > >> >>   * RPMB_IOC_RBLOCKS_CMD - read blocks from device
> > >> >>
> > >> >> The keys used for programming and writing blocks to the device are
> > >> >> key_serial_t handles as provided by the keyctl() interface.
> > >> >>
> > >> >> [AJB: here there are two key differences between this and the
> > >> >> original proposal. The first is the dropping of the sequence of
> > >> >> preformated frames in favour of explicit actions. The second is
> > >> >> the introduction of key_serial_t and the keyring API for
> > >> >> referencing the key to use]
> > >> >
> > >> > Putting it gently I'm not sure this is good idea, from the security
> > >> > point of
> > >> view.
> > >> > The key has to be possession of the one that signs the frames as
> > >> > they are,
> > >> it doesn't mean it is linux kernel keyring, it can be other party on
> > >> different system.
> > >> > With this approach you will make the other usecases not applicable.
> > >> > It is less then trivial to move key securely from one system to another.
> > >>
> > >> OK I can understand the desire for such a use-case but it does
> > >> constrain the interface on the kernel with access to the hardware to
> > >> purely providing a pipe to the raw hardware while also having to
> > >> expose the details of the HW to userspace.
> > > This is the use case in Android. The key is in the "trusty" which
> > > different os running in a virtual environment. The file storage
> > > abstraction is implemented there. I'm not sure the point of
> > > constraining the kernel, can you please elaborate on that.
> >
> > Well the kernel is all about abstracting differences not baking in assumptions.
> > However can I ask a bit more about this security model?
> > Is the secure enclave just a separate userspace process or is it in a separate
> > virtual machine? Is it accessible at all by the kernel running the driver?
>
> It's not an assumption this is working for few years already (https://source.android.com/security/trusty#application_services)
> The model is that you have a trusted environment (TEE)  in which can be in any of the form you described above.
> And there is established agreement via the RPMB key that TEE is only entity that can produce content to be stored on RPBM,
> The RPMB hardware also ensure that nobody can catch it in the middle and replay that storage event.
>
> My point is that interface you are suggesting is not covering all possible usages of RPMB, actually usages that are already in place.

It turned out that the application that we (Linaro) need does have the
same requirements and needs to store the key in a TEE, transferring
the message with the MAC into the kernel, rather than keeping the
key stored in user space or kernel.

However, after I had a look at the nvme-rpmb user space implementation,
I found that this is different, and always expects the key to be stored
in a local file:
https://github.com/linux-nvme/nvme-cli/blob/master/nvme-rpmb.c#L878

This both works with the same kernel interface though, as the kernel
would still get the data along with the HMAC, rather than having the key
stored in the kernel, but it does mean that the frame gets passed to
the kernel in a device specific layout, with at least nvme using an incompatible
layout from everything else.

        Arnd

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

* RE: [RFC PATCH 2/5] char: rpmb: provide a user space interface
  2021-03-04 21:43             ` Arnd Bergmann
@ 2021-03-05  6:31               ` Winkler, Tomas
  0 siblings, 0 replies; 47+ messages in thread
From: Winkler, Tomas @ 2021-03-05  6:31 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Alex Bennée, linux-kernel, maxim.uvarov, joakim.bech,
	ilias.apalodimas, ruchika.gupta, Huang, Yang, Zhu, Bing,
	Matti.Moell, hmo, linux-mmc, linux-scsi, linux-nvme, Ulf Hansson,
	Linus Walleij, Arnd Bergmann, Usyskin, Alexander, Avri Altman

> On Thu, Mar 4, 2021 at 8:54 PM Winkler, Tomas <tomas.winkler@intel.com>
> wrote:
> > > Winkler, Tomas <tomas.winkler@intel.com> writes:
> > > >> "Winkler, Tomas" <tomas.winkler@intel.com> writes:
> > > >>
> > > >> >> The user space API is achieved via a number of synchronous
> IOCTLs.
> > > >> >>
> > > >> >>   * RPMB_IOC_VER_CMD - simple versioning API
> > > >> >>   * RPMB_IOC_CAP_CMD - query of underlying capabilities
> > > >> >>   * RPMB_IOC_PKEY_CMD - one time programming of access key
> > > >> >>   * RPMB_IOC_COUNTER_CMD - query the write counter
> > > >> >>   * RPMB_IOC_WBLOCKS_CMD - write blocks to device
> > > >> >>   * RPMB_IOC_RBLOCKS_CMD - read blocks from device
> > > >> >>
> > > >> >> The keys used for programming and writing blocks to the device
> > > >> >> are key_serial_t handles as provided by the keyctl() interface.
> > > >> >>
> > > >> >> [AJB: here there are two key differences between this and the
> > > >> >> original proposal. The first is the dropping of the sequence
> > > >> >> of preformated frames in favour of explicit actions. The
> > > >> >> second is the introduction of key_serial_t and the keyring API
> > > >> >> for referencing the key to use]
> > > >> >
> > > >> > Putting it gently I'm not sure this is good idea, from the
> > > >> > security point of
> > > >> view.
> > > >> > The key has to be possession of the one that signs the frames
> > > >> > as they are,
> > > >> it doesn't mean it is linux kernel keyring, it can be other party
> > > >> on different system.
> > > >> > With this approach you will make the other usecases not applicable.
> > > >> > It is less then trivial to move key securely from one system to
> another.
> > > >>
> > > >> OK I can understand the desire for such a use-case but it does
> > > >> constrain the interface on the kernel with access to the hardware
> > > >> to purely providing a pipe to the raw hardware while also having
> > > >> to expose the details of the HW to userspace.
> > > > This is the use case in Android. The key is in the "trusty" which
> > > > different os running in a virtual environment. The file storage
> > > > abstraction is implemented there. I'm not sure the point of
> > > > constraining the kernel, can you please elaborate on that.
> > >
> > > Well the kernel is all about abstracting differences not baking in
> assumptions.
> > > However can I ask a bit more about this security model?
> > > Is the secure enclave just a separate userspace process or is it in
> > > a separate virtual machine? Is it accessible at all by the kernel running the
> driver?
> >
> > It's not an assumption this is working for few years already
> > (https://source.android.com/security/trusty#application_services)
> > The model is that you have a trusted environment (TEE)  in which can be in
> any of the form you described above.
> > And there is established agreement via the RPMB key that TEE is only
> > entity that can produce content to be stored on RPBM, The RPMB
> hardware also ensure that nobody can catch it in the middle and replay that
> storage event.
> >
> > My point is that interface you are suggesting is not covering all possible
> usages of RPMB, actually usages that are already in place.
> 
> It turned out that the application that we (Linaro) need does have the same
> requirements and needs to store the key in a TEE, transferring the message
> with the MAC into the kernel, rather than keeping the key stored in user
> space or kernel.
> 
> However, after I had a look at the nvme-rpmb user space implementation, I
> found that this is different, and always expects the key to be stored in a local
> file:
> https://github.com/linux-nvme/nvme-cli/blob/master/nvme-rpmb.c#L878


This doesn't make it very safe

> This both works with the same kernel interface though, as the kernel would
> still get the data along with the HMAC, rather than having the key stored in
> the kernel, but it does mean that the frame gets passed to the kernel in a
> device specific layout, with at least nvme using an incompatible layout from
> everything else.

NVMe is not by JEDEC so this layout is different and there are some changes but the overall storage operations are the same story.
 I do have a solution also for NVME inclusion into the framework. I haven't published that part yet.  
It won't support virtio part,  as this requires some legal work to include that into  virtio spec.

Thanks
Tomas


 
>         Arnd

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-04 20:56   ` Arnd Bergmann
@ 2021-03-05  7:51     ` Joakim Bech
  2021-03-05  8:44       ` Arnd Bergmann
  0 siblings, 1 reply; 47+ messages in thread
From: Joakim Bech @ 2021-03-05  7:51 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Alex Bennée, linux-kernel, Maxim Uvarov, Ilias Apalodimas,
	ruchika.gupta, Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell,
	hmo, linux-mmc, linux-scsi, linux-nvme, Ulf Hansson,
	Linus Walleij, Arnd Bergmann

On Thu, Mar 04, 2021 at 09:56:24PM +0100, Arnd Bergmann wrote:
> On Wed, Mar 3, 2021 at 2:54 PM Alex Bennée <alex.bennee@linaro.org> wrote:
> >
> > A number of storage technologies support a specialised hardware
> > partition designed to be resistant to replay attacks. The underlying
> > HW protocols differ but the operations are common. The RPMB partition
> > cannot be accessed via standard block layer, but by a set of specific
> > commands: WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY. Such a
> > partition provides authenticated and replay protected access, hence
> > suitable as a secure storage.
> >
> > The RPMB layer aims to provide in-kernel API for Trusted Execution
> > Environment (TEE) devices that are capable to securely compute block
> > frame signature. In case a TEE device wishes to store a replay
> > protected data, requests the storage device via RPMB layer to store
> > the data.
> >
> > A TEE device driver can claim the RPMB interface, for example, via
> > class_interface_register(). The RPMB layer provides a series of
> > operations for interacting with the device.
> >
> >   * program_key - a one time operation for setting up a new device
> >   * get_capacity - introspect the device capacity
> >   * get_write_count - check the write counter
> >   * write_blocks - write a series of blocks to the RPMB device
> >   * read_blocks - read a series of blocks from the RPMB device
> 
> Based on the discussion we had today in a meeting, it seems the
> main change that is needed is to get back to the original model
> of passing the encrypted data to the kernel instead of cleartext
> data, as the main use case we know of is to have the key inside of
> the TEE device and not available to the kernel or user space.
> 
Yes, for OP-TEE we have to encrypt all data going to RPMB, since the
information goes via non-secure world. We get the integrity by applying
the HMAC with the key that is being discussed in this thread. The TEE
owns and is responsible for programming the key (and that should be
something that is achieved as part of the manufacturing process).

> This is also required to be able to forward the encrypted data
> through the same interface on a KVM host, when the guest
> uses virtio-rpmb, and the host forwards the data into an mmc or
> ufs device.
> 
> That said, I can also imagine use cases where we do want to
> store the key in the kernel's keyring, so maybe we end up needing
> both.
> 
The concern I have in those cases is that you need to share the RPMB key
in some way if you need to access the RPMB device from secure side as
well as from the non-secure side. Technically doable I guess, but in
practice and in terms of security it doesn't seem like a good approach.

In a shared environment like that you also have the problem that you
need to agree on how to actually store files on the RPMB device. OP-TEE
has it's own "FAT-look-a-like" implementation when using RPMB. But if
you need mutual access, then you need to get into agreement on where to
actually store the files in the RPMB.

However, if secure side for some reason doesn't use RPMB at all, then
kernel could of course take control of it and use it.

I would probably not spend too much time on taking that use case into
account until we actually see a real need for it.

> > The detailed operation of implementing the access is left to the TEE
> > device driver itself.
> >
> > [This is based-on Thomas Winkler's proposed API from:
> >
> >   https://lore.kernel.org/linux-mmc/1478548394-8184-2-git-send-email-tomas.winkler@intel.com/
> >
> > The principle difference is the framing details and HW specific
> > bits (JDEC vs NVME frames) are left to the lower level TEE driver to
> > worry about. The eventual userspace ioctl interface will aim to be
> > similarly generic. This is an RFC to follow up on:
> >
> >   Subject: RPMB user space ABI
> >   Date: Thu, 11 Feb 2021 14:07:00 +0000
> >   Message-ID: <87mtwashi4.fsf@linaro.org>]
> >
> > Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> > Cc: Tomas Winkler <tomas.winkler@intel.com>
> > Cc: Ulf Hansson <ulf.hansson@linaro.org>
> > Cc: Linus  Walleij <linus.walleij@linaro.org>
> > Cc: Arnd Bergmann <arnd.bergmann@linaro.org>
> > Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> > ---
> >  MAINTAINERS                |   7 +
> >  drivers/char/Kconfig       |   2 +
> >  drivers/char/Makefile      |   1 +
> >  drivers/char/rpmb/Kconfig  |  11 +
> >  drivers/char/rpmb/Makefile |   7 +
> >  drivers/char/rpmb/core.c   | 429 +++++++++++++++++++++++++++++++++++++
> >  include/linux/rpmb.h       | 163 ++++++++++++++
> 
> 
> My feeling is that it should be a top-level subsystem, in drivers/rpmb
> rather than drivers/char/rpmb, as you implement an abstraction layer
> that other drivers can plug into, rather than a simple driver.
> 
>        Arnd

-- 
Regards,
Joakim

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-05  7:51     ` Joakim Bech
@ 2021-03-05  8:44       ` Arnd Bergmann
  2021-03-08 16:20         ` Linus Walleij
  2021-03-09 17:12         ` David Howells
  0 siblings, 2 replies; 47+ messages in thread
From: Arnd Bergmann @ 2021-03-05  8:44 UTC (permalink / raw)
  To: Joakim Bech
  Cc: Alex Bennée, linux-kernel, Maxim Uvarov, Ilias Apalodimas,
	ruchika.gupta, Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell,
	hmo, linux-mmc, linux-scsi, linux-nvme, Ulf Hansson,
	Linus Walleij, Arnd Bergmann

On Fri, Mar 5, 2021 at 8:52 AM Joakim Bech <joakim.bech@linaro.org> wrote:
> On Thu, Mar 04, 2021 at 09:56:24PM +0100, Arnd Bergmann wrote:
> > On Wed, Mar 3, 2021 at 2:54 PM Alex Bennée <alex.bennee@linaro.org> wrote:
> > That said, I can also imagine use cases where we do want to
> > store the key in the kernel's keyring, so maybe we end up needing
> > both.
> >
> The concern I have in those cases is that you need to share the RPMB key
> in some way if you need to access the RPMB device from secure side as
> well as from the non-secure side. Technically doable I guess, but in
> practice and in terms of security it doesn't seem like a good approach.
>
> In a shared environment like that you also have the problem that you
> need to agree on how to actually store files on the RPMB device. OP-TEE
> has it's own "FAT-look-a-like" implementation when using RPMB. But if
> you need mutual access, then you need to get into agreement on where to
> actually store the files in the RPMB.
>
> However, if secure side for some reason doesn't use RPMB at all, then
> kernel could of course take control of it and use it.
>
> I would probably not spend too much time on taking that use case into
> account until we actually see a real need for it.

I think the scenario for the 'nvme-rpmb' tool that does the signing in user
space does not involve any TEE at the moment, because PCs usually
don't have one.

I agree that sharing the RPMB is not a great idea, so if you have a TEE
in the system that requires an RPMB for storage, it won't be usable by
anything else. However, you can have multiple RPMB partitions with separate
keys on an NVMe drive, and you can easily have multiple emulated
virtio-rpmb devices  in a guest and use them for purposes other than the
TEE.

      Arnd

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-05  8:44       ` Arnd Bergmann
@ 2021-03-08 16:20         ` Linus Walleij
  2021-03-09 21:09           ` Hector Martin
  2021-03-09 17:12         ` David Howells
  1 sibling, 1 reply; 47+ messages in thread
From: Linus Walleij @ 2021-03-08 16:20 UTC (permalink / raw)
  To: Arnd Bergmann, keyrings, David Howells, Jarkko Sakkinen
  Cc: Joakim Bech, Alex Bennée, linux-kernel, Maxim Uvarov,
	Ilias Apalodimas, ruchika.gupta, Winkler, Tomas, yang.huang,
	bing.zhu, Matti.Moell, hmo, linux-mmc, linux-scsi, linux-nvme,
	Ulf Hansson, Arnd Bergmann, Hector Martin

On Fri, Mar 5, 2021 at 9:44 AM Arnd Bergmann <arnd@linaro.org> wrote:

> I think the scenario for the 'nvme-rpmb' tool that does the signing in user
> space does not involve any TEE at the moment, because PCs usually
> don't have one.

Isn't that because (Windows-)PC:s prefer to use TPMs which
include their own key storage?

Apple has their "secure enclave" (separate security chip) and as illustrated
by Marcan it did not make use of RPMB as of 2016:
https://marcan.st/2016/03/untangling-ios-pin-code-security/
Maybe they have since started to use it? (They should.)

AFAICT the use case for RPMB is:
1. Used by Android with some TEE, and if you're not running
    Android and some TEE then
2. Use it for whatever you like

As it seems neither Microsoft nor Apple is paying it much attention
(+/- new facts) it will be up to the community to define use cases
for RPMB. I don't know what would make most sense, but the
kernel keyring seems to make a bit of sense as it is a well maintained
keyring project.

So the proposal is to (as some goal) bridge the keyring subsystem
to the proposed RPMB subsystem with an kernel-internal API.

What do the keyring people think of this? Added David & Jarkko to the
thread to get some input.

I suppose it would be a bit brutal if the kernel would just go in and
appropriate any empty RPMB it finds, but I suspect it is the right way
to make use of this facility given that so many of them are just sitting
there unused. Noone will run $CUSTOM_UTILITY any more than they
run the current RPMB tools in mmc-tools.

Agreeing with OP-TEE on the format and management of
any one RPMB partition seems like a good idea, if for nothing else
then for sharing documentation.

> I agree that sharing the RPMB is not a great idea, so if you have a TEE
> in the system that requires an RPMB for storage, it won't be usable by
> anything else. However, you can have multiple RPMB partitions with separate
> keys on an NVMe drive, and you can easily have multiple emulated
> virtio-rpmb devices  in a guest and use them for purposes other than the
> TEE.

The eMMC RPMB code even handles multiple RPMB partitions on an
eMMC. But I don't think I have ever seen a device with more than
one RPMB.

Yours,
Linus Walleij

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

* [RFC PATCH  0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend
  2021-03-03 13:54 [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend Alex Bennée
                   ` (4 preceding siblings ...)
  2021-03-03 13:55 ` [RFC PATCH 5/5] tools/rpmb: simple test sequence Alex Bennée
@ 2021-03-09 13:27 ` Avri Altman
  2021-03-10 14:29   ` Alex Bennée
  5 siblings, 1 reply; 47+ messages in thread
From: Avri Altman @ 2021-03-09 13:27 UTC (permalink / raw)
  To: alex.bennee
  Cc: Matti.Moell, arnd, bing.zhu, hmo, ilias.apalodimas, joakim.bech,
	linux-kernel, linux-mmc, linux-nvme, linux-scsi, maxim.uvarov,
	ruchika.gupta, tomas.winkler, yang.huang

The mmc driver has some hooks to support rpmb access, but access is
mainly facilitated from user space, e.g. mmc-utils.

The ufs driver has no concept of rpmb access - it is facilitated via
user space, e.g. ufs-utils and similar.

Both for ufs and mmc, rpmb access is defined in their applicable jedec
specs. This is the case for few years now - AFAIK No new rpmb-related
stuff is expected to be introduced in the near future.

What problems, as far as mmc and ufs, are you trying to solve by this new subsystem?

Thanks,
Avri


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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-05  8:44       ` Arnd Bergmann
  2021-03-08 16:20         ` Linus Walleij
@ 2021-03-09 17:12         ` David Howells
  2021-03-10  4:54           ` Sumit Garg
  2021-03-10  9:33           ` Linus Walleij
  1 sibling, 2 replies; 47+ messages in thread
From: David Howells @ 2021-03-09 17:12 UTC (permalink / raw)
  To: Linus Walleij
  Cc: dhowells, Arnd Bergmann, keyrings, Jarkko Sakkinen, Joakim Bech,
	Alex Bennée, linux-kernel, Maxim Uvarov, Ilias Apalodimas,
	ruchika.gupta, Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell,
	hmo, linux-mmc, linux-scsi, linux-nvme, Ulf Hansson,
	Arnd Bergmann, Hector Martin

Linus Walleij <linus.walleij@linaro.org> wrote:

> As it seems neither Microsoft nor Apple is paying it much attention
> (+/- new facts) it will be up to the community to define use cases
> for RPMB. I don't know what would make most sense, but the
> kernel keyring seems to make a bit of sense as it is a well maintained
> keyring project.

I'm afraid I don't know a whole lot about the RPMB.  I've just been and read
https://lwn.net/Articles/682276/ about it.

What is it you envision the keyring API doing with regard to this?  Being used
to represent the key needed to access the RPMB or being used to represent an
RPMB entry (does it have entries?)?

David


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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-08 16:20         ` Linus Walleij
@ 2021-03-09 21:09           ` Hector Martin
  2021-03-10  5:14             ` Sumit Garg
  0 siblings, 1 reply; 47+ messages in thread
From: Hector Martin @ 2021-03-09 21:09 UTC (permalink / raw)
  To: Linus Walleij, Arnd Bergmann, keyrings, David Howells, Jarkko Sakkinen
  Cc: Joakim Bech, Alex Bennée, linux-kernel, Maxim Uvarov,
	Ilias Apalodimas, ruchika.gupta, Winkler, Tomas, yang.huang,
	bing.zhu, Matti.Moell, hmo, linux-mmc, linux-scsi, linux-nvme,
	Ulf Hansson, Arnd Bergmann

On 09/03/2021 01.20, Linus Walleij wrote:
> I suppose it would be a bit brutal if the kernel would just go in and
> appropriate any empty RPMB it finds, but I suspect it is the right way
> to make use of this facility given that so many of them are just sitting
> there unused. Noone will run $CUSTOM_UTILITY any more than they
> run the current RPMB tools in mmc-tools.

AIUI the entire thing relies on a shared key that is programmed once 
into the RPMB device, which is a permanent operation. This key has to be 
secure, usually stored on CPU fuses or derived based on such a root of 
trust. To me it would seem ill-advised to attempt to automate this 
process and have the kernel do a permanent take-over of any RPMBs it 
finds (with what key, for one?) :)

For what it's worth, these days I think Apple uses a separate, dedicated 
secure element for replay protected storage, not RPMB. That seems like a 
sane approach, given that obviously Flash storage vendors cannot be 
trusted to write security-critical firmware. But if all you have is 
RPMB, using it is better than nothing.

The main purpose of the RPMB is, as the name implies, replay protection. 
You can do secure storage on any random flash with encryption, and even 
do full authentication with hash trees, but the problem is no matter how 
fancy your scheme is, attackers can always dump all memory and roll your 
device back to the past. This defeats stuff like PIN code attempt 
limits. So it isn't so much for storing crypto keys or such, but rather 
a way to prevent these attacks.

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-09 17:12         ` David Howells
@ 2021-03-10  4:54           ` Sumit Garg
  2021-03-10  9:33           ` Linus Walleij
  1 sibling, 0 replies; 47+ messages in thread
From: Sumit Garg @ 2021-03-10  4:54 UTC (permalink / raw)
  To: David Howells
  Cc: Linus Walleij, Arnd Bergmann, open list:ASYMMETRIC KEYS,
	Jarkko Sakkinen, Joakim Bech, Alex Bennée, linux-kernel,
	Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta, Winkler, Tomas,
	yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc, linux-scsi,
	linux-nvme, Ulf Hansson, Arnd Bergmann, Hector Martin

Hi David,

On Tue, 9 Mar 2021 at 22:43, David Howells <dhowells@redhat.com> wrote:
>
> Linus Walleij <linus.walleij@linaro.org> wrote:
>
> > As it seems neither Microsoft nor Apple is paying it much attention
> > (+/- new facts) it will be up to the community to define use cases
> > for RPMB. I don't know what would make most sense, but the
> > kernel keyring seems to make a bit of sense as it is a well maintained
> > keyring project.
>
> I'm afraid I don't know a whole lot about the RPMB.  I've just been and read
> https://lwn.net/Articles/682276/ about it.
>
> What is it you envision the keyring API doing with regard to this?  Being used
> to represent the key needed to access the RPMB or being used to represent an
> RPMB entry (does it have entries?)?
>

I think it's the former one to represent the RPMB key and it looks
like the trusted and encrypted keys subsystem should be useful here to
prevent any user-space exposures of the RPMB key.

-Sumit

> David
>

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-09 21:09           ` Hector Martin
@ 2021-03-10  5:14             ` Sumit Garg
  2021-03-10  8:47               ` Hector Martin
  0 siblings, 1 reply; 47+ messages in thread
From: Sumit Garg @ 2021-03-10  5:14 UTC (permalink / raw)
  To: Hector Martin, Linus Walleij
  Cc: Arnd Bergmann, open list:ASYMMETRIC KEYS, David Howells,
	Jarkko Sakkinen, Joakim Bech, Alex Bennée, linux-kernel,
	Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta, Winkler, Tomas,
	yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc, linux-scsi,
	linux-nvme, Ulf Hansson, Arnd Bergmann

On Wed, 10 Mar 2021 at 02:47, Hector Martin <marcan@marcan.st> wrote:
>
> On 09/03/2021 01.20, Linus Walleij wrote:
> > I suppose it would be a bit brutal if the kernel would just go in and
> > appropriate any empty RPMB it finds, but I suspect it is the right way
> > to make use of this facility given that so many of them are just sitting
> > there unused. Noone will run $CUSTOM_UTILITY any more than they
> > run the current RPMB tools in mmc-tools.
>
> AIUI the entire thing relies on a shared key that is programmed once
> into the RPMB device, which is a permanent operation. This key has to be
> secure, usually stored on CPU fuses or derived based on such a root of
> trust. To me it would seem ill-advised to attempt to automate this
> process and have the kernel do a permanent take-over of any RPMBs it
> finds (with what key, for one?) :)
>

Wouldn't it be a good idea to use DT here to represent whether a
particular RPMB is used as a TEE backup or is available for normal
kernel usage?

In case of normal kernel usage, I think the RPMB key can come from
trusted and encrypted keys subsystem.

-Sumit

> For what it's worth, these days I think Apple uses a separate, dedicated
> secure element for replay protected storage, not RPMB. That seems like a
> sane approach, given that obviously Flash storage vendors cannot be
> trusted to write security-critical firmware. But if all you have is
> RPMB, using it is better than nothing.
>
> The main purpose of the RPMB is, as the name implies, replay protection.
> You can do secure storage on any random flash with encryption, and even
> do full authentication with hash trees, but the problem is no matter how
> fancy your scheme is, attackers can always dump all memory and roll your
> device back to the past. This defeats stuff like PIN code attempt
> limits. So it isn't so much for storing crypto keys or such, but rather
> a way to prevent these attacks.
>
> --
> Hector Martin (marcan@marcan.st)
> Public Key: https://mrcn.st/pub

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-10  5:14             ` Sumit Garg
@ 2021-03-10  8:47               ` Hector Martin
  2021-03-10  9:48                 ` Linus Walleij
  2021-03-10 10:29                 ` Sumit Garg
  0 siblings, 2 replies; 47+ messages in thread
From: Hector Martin @ 2021-03-10  8:47 UTC (permalink / raw)
  To: Sumit Garg, Linus Walleij
  Cc: Arnd Bergmann, open list:ASYMMETRIC KEYS, David Howells,
	Jarkko Sakkinen, Joakim Bech, Alex Bennée, linux-kernel,
	Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta, Winkler, Tomas,
	yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc, linux-scsi,
	linux-nvme, Ulf Hansson, Arnd Bergmann

On 10/03/2021 14.14, Sumit Garg wrote:
> On Wed, 10 Mar 2021 at 02:47, Hector Martin <marcan@marcan.st> wrote:
>>
>> On 09/03/2021 01.20, Linus Walleij wrote:
>>> I suppose it would be a bit brutal if the kernel would just go in and
>>> appropriate any empty RPMB it finds, but I suspect it is the right way
>>> to make use of this facility given that so many of them are just sitting
>>> there unused. Noone will run $CUSTOM_UTILITY any more than they
>>> run the current RPMB tools in mmc-tools.
>>
>> AIUI the entire thing relies on a shared key that is programmed once
>> into the RPMB device, which is a permanent operation. This key has to be
>> secure, usually stored on CPU fuses or derived based on such a root of
>> trust. To me it would seem ill-advised to attempt to automate this
>> process and have the kernel do a permanent take-over of any RPMBs it
>> finds (with what key, for one?) :)
>>
> 
> Wouldn't it be a good idea to use DT here to represent whether a
> particular RPMB is used as a TEE backup or is available for normal
> kernel usage?
> 
> In case of normal kernel usage, I think the RPMB key can come from
> trusted and encrypted keys subsystem.

Remember that if the key is ever lost, the RPMB is now completely 
useless forever.

This is why, as far as I know, most sane platforms will use hard fused 
values to derive this kind of thing, not any kind of key stored in 
erasable storage.

Also, newly provisioned keys are sent in plain text, which means that 
any kind of "if the RPMB is blank, take it over" automation equates to 
handing over your key who an attacker who removes the RPMB and replaces 
it with a blank one, and then they can go access anything they want on 
the old RPMB device (assuming the key hasn't changed; and if it has 
changed that's conversely a recipe for data loss if something goes wrong).

I really think trying to automate any kind of "default" usage of an RPMB 
is a terrible idea. It needs to be a conscious decision on a 
per-platform basis.

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-09 17:12         ` David Howells
  2021-03-10  4:54           ` Sumit Garg
@ 2021-03-10  9:33           ` Linus Walleij
  1 sibling, 0 replies; 47+ messages in thread
From: Linus Walleij @ 2021-03-10  9:33 UTC (permalink / raw)
  To: David Howells
  Cc: Arnd Bergmann, keyrings, Jarkko Sakkinen, Joakim Bech,
	Alex Bennée, linux-kernel, Maxim Uvarov, Ilias Apalodimas,
	Ruchika Gupta, Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell,
	hmo, linux-mmc, linux-scsi, linux-nvme, Ulf Hansson,
	Arnd Bergmann, Hector Martin

On Tue, Mar 9, 2021 at 6:12 PM David Howells <dhowells@redhat.com> wrote:
> Linus Walleij <linus.walleij@linaro.org> wrote:
>
> > As it seems neither Microsoft nor Apple is paying it much attention
> > (+/- new facts) it will be up to the community to define use cases
> > for RPMB. I don't know what would make most sense, but the
> > kernel keyring seems to make a bit of sense as it is a well maintained
> > keyring project.
>
> I'm afraid I don't know a whole lot about the RPMB.  I've just been and read
> https://lwn.net/Articles/682276/ about it.

Sorry, here is a primer on RPMB.

The proper source is the eMMC specification from JEDEC
which has semi-open access:
https://www.jedec.org/standards-documents/technology-focus-areas/flash-memory-ssds-ufs-emmc/e-mmc

The spec is not super helpful because it does not describe what the
intention or use case for RPMB is, just what commands it can be
given.

Western Digital describes the use cases in this whitepaper page 5 ff:
https://documents.westerndigital.com/content/dam/doc-library/en_us/assets/public/western-digital/collateral/white-paper/white-paper-emmc-security.pdf

Quote:
"Some well-known use cases include software version
authentication, fingerprint verification, secure key storage,
network vendor information, digital rights management (DRM)
and secure payments."

The replay protected memory block comes from mobile phone
vendors, and it is described as designed for a usecase known
as "anti-rollback": make it impossible to flash an older firmware.
This is achieved by monotonic counters: a hardware counter
that always increases so that if we have software version 13
flashed we can flash version 14 or 15 but not version 10 or 12.
Attackers of mobile phones used the possibility to revert to
old firmware with vulnerabilities as an attack vector.

Messages to the RPMB are protected by a symmetric key
which is 32 bytes long. The hash used in messaging is
HMAC SHA-256.

The symmetric key is written once to initialize the RPMB.
With the current mmc-utils "mmc" command it looks like this:

echo -n AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH | mmc rpmb write-key /dev/mmcblk0rpmb -

The entity writing stuff to RPMB needs to keep track of this
secret. This is why a secure world such as TEE is often using
RPMB, as these usually have access to a protected secret
key, but any trusted environment can use the mechanism.
Compared to TPM, we are on the inside of the chip here,
so the agent dealing with this secret key will be vulnerable.

After this secret has been initialized, protected data blocks of 256
bytes can be written to RPMB while providing the key likt this:

(awk 'BEGIN {while (c++<256) printf "a"}' | echo -n
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH) | mmc rpmb write-block
/dev/mmcblk0rpmb 0x02 - -

0x02 is the *counter*, so if you after this try to send the message
with 0x01 it will fail, whereas 0x03 will work. That is how the
monotonic counter is specified in the write interactions.

This can be imagined as writing keys 1, 2, 3 ... while you cannot
overwrite an older key you can write the next one in sequence.
Typically this would be the version number of a firmware.
The 256 bytes of data sent along with the key number is
typically the hash of a firmware. But it can be any 256 bytes
of data, RPMB leaves this up to whoever implements it.

You can also read chunks of 256 bytes from the device:
echo -n AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH | mmc rpmb read-block
/dev/mmcblk0rpmb 0x02 1 /tmp/block -

(0x02 again is the key index, 1 is the number of blocks/keys
we want to read)

This protocol is challenge-response so a random session key
will be used along with the MAC for authentication.

It is possible to read a key without authentication. I don't know
what the use case of this would be:

mmc rpmb read-block /dev/mmcblk0rpmb 0x02 1 /tmp/block

RPMB is a multiple of 128KB of key storage. Most typically
it is that size, so 128KB/256 = 512 unique keys can be
written in most standard parts.

> What is it you envision the keyring API doing with regard to this?
> Being used to represent the key needed to access the RPMB or
> being used to represent an RPMB entry (does it have entries?)?

The idea is to have an API toward RPMB that keyring can
use to store replay protection or other monotonic sequence
information. Only one party can hold the authentication key
so I guess both.

The most intuitive use case is protecting against exhaustive
password/pin/fingerprint/other authentication token search.

On mobile phones it is used to establish that 3 attempts is really
3 attempts, then your device is locked, for example. Doesn't
have to be 3. Can be 500. But to put a cap on it.

Also a time stamp from a monotonic clock can be stored in
RPMB so that the increasing time between unlock attempts
is enforced and cannot be manipulated. This requires
secure, monotonic time (which can be achieved in various
ways).

Is this something keyring does today, or would be doing
in the future? (Sorry for my ignorance...)

The original use case of being unable to install older
software can also be done, but since Linux distributions
generally support installing older packages I don't think
this is going to be requested much, maybe Chromebooks
and Androids would appreciate to do that through this
mechanism though?

Yours,
Linus Walleij

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-10  8:47               ` Hector Martin
@ 2021-03-10  9:48                 ` Linus Walleij
  2021-03-10 13:52                   ` Hector Martin
  2021-03-10 10:29                 ` Sumit Garg
  1 sibling, 1 reply; 47+ messages in thread
From: Linus Walleij @ 2021-03-10  9:48 UTC (permalink / raw)
  To: Hector Martin, David Howells, keyrings, Jarkko Sakkinen
  Cc: Sumit Garg, Arnd Bergmann, Joakim Bech, Alex Bennée,
	linux-kernel, Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta,
	Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Arnd Bergmann

On Wed, Mar 10, 2021 at 9:47 AM Hector Martin <marcan@marcan.st> wrote:

> Remember that if the key is ever lost, the RPMB is now completely
> useless forever.
>
> This is why, as far as I know, most sane platforms will use hard fused
> values to derive this kind of thing, not any kind of key stored in
> erasable storage.

You're right. In the mobile phone world this is a given fact.

If we are thinking devices are to be repurposed or reinstalled
from scratch for example, like ordinary desktops or servers,
RPMB does not make generic sense: it is not for
"generic computing" but rather for protecting devices that you
carry around and can be lost: mobile phones, chromebooks,
maybe laptops.

If and only if the user so desires, I would say, but sometimes
the vendors decide policy...

(+/- the fact that some recent supply chain attacks for server
software may actually make cloud people start thinking like this
about their servers integrity, what do I know.)

> Also, newly provisioned keys are sent in plain text, which means that
> any kind of "if the RPMB is blank, take it over" automation equates to
> handing over your key who an attacker who removes the RPMB and replaces
> it with a blank one, and then they can go access anything they want on
> the old RPMB device (assuming the key hasn't changed; and if it has
> changed that's conversely a recipe for data loss if something goes wrong).
>
> I really think trying to automate any kind of "default" usage of an RPMB
> is a terrible idea. It needs to be a conscious decision on a
> per-platform basis.

OK sorry for my bad ideas, what was I thinking :D

For a laptop or so, I would say, a user who is paranoid that their
device gets stolen and used by someone else, should be able to
set their device up, with some tool, such that a secret key from
somewhere and RPMB is used to lock down the machine so that
attackers cannot get into it and get the data out.

Disk is encrypted, and RPMB is there to block any exhaustive
password or other authentication token search.

Ideally: the only way to make use of the hardware again would
be to solder off the eMMC, if eMMC is used for RPMB.
If we have RPMB on an NVME or UFS drive, the idea is
to lock that thing such that it becomes useless and need to
be replaced with a new part in this scenario.

In practice: make it hard, because we know no such jail is
perfect. Make it not worth the effort, make it cheaper for thieves
to just buy a new harddrive to use a stolen laptop, locking
the data that was in it away forever by making the drive
useless for any practical attacks.

Maybe it will be possible to blank the drive and use without
RPMB since that is now locked with a key they can no longer
acces: the end result is the same: RPMB protected the data
of the original user. So a one-time user protection such
as a seal, once broken this seal cannot be reused to seal
anything again and that is OK.

Yours,
Linus Walleij

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-10  8:47               ` Hector Martin
  2021-03-10  9:48                 ` Linus Walleij
@ 2021-03-10 10:29                 ` Sumit Garg
  2021-03-11  0:49                   ` Linus Walleij
  1 sibling, 1 reply; 47+ messages in thread
From: Sumit Garg @ 2021-03-10 10:29 UTC (permalink / raw)
  To: Hector Martin
  Cc: Linus Walleij, Arnd Bergmann, open list:ASYMMETRIC KEYS,
	David Howells, Jarkko Sakkinen, Joakim Bech, Alex Bennée,
	linux-kernel, Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta,
	Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Arnd Bergmann

On Wed, 10 Mar 2021 at 14:17, Hector Martin <marcan@marcan.st> wrote:
>
> On 10/03/2021 14.14, Sumit Garg wrote:
> > On Wed, 10 Mar 2021 at 02:47, Hector Martin <marcan@marcan.st> wrote:
> >>
> >> On 09/03/2021 01.20, Linus Walleij wrote:
> >>> I suppose it would be a bit brutal if the kernel would just go in and
> >>> appropriate any empty RPMB it finds, but I suspect it is the right way
> >>> to make use of this facility given that so many of them are just sitting
> >>> there unused. Noone will run $CUSTOM_UTILITY any more than they
> >>> run the current RPMB tools in mmc-tools.
> >>
> >> AIUI the entire thing relies on a shared key that is programmed once
> >> into the RPMB device, which is a permanent operation. This key has to be
> >> secure, usually stored on CPU fuses or derived based on such a root of
> >> trust. To me it would seem ill-advised to attempt to automate this
> >> process and have the kernel do a permanent take-over of any RPMBs it
> >> finds (with what key, for one?) :)
> >>
> >
> > Wouldn't it be a good idea to use DT here to represent whether a
> > particular RPMB is used as a TEE backup or is available for normal
> > kernel usage?
> >
> > In case of normal kernel usage, I think the RPMB key can come from
> > trusted and encrypted keys subsystem.
>
> Remember that if the key is ever lost, the RPMB is now completely
> useless forever.
>
> This is why, as far as I know, most sane platforms will use hard fused
> values to derive this kind of thing, not any kind of key stored in
> erasable storage.

AFAIK, trusted and encrypted keys are generally loaded from initramfs
(as an encrypted blob) which happens during boot and if an attacker is
able to erase initramfs then it's already able to make the device
non-bootable (DoS attack which is hard to prevent against).

Although, I agree with you that fuses are the preferred way to store
RPMB key but not every platform may possess it and vendors may decide
to re-flash a bricked device via recovery image.

>
> Also, newly provisioned keys are sent in plain text, which means that
> any kind of "if the RPMB is blank, take it over" automation equates to
> handing over your key who an attacker who removes the RPMB and replaces
> it with a blank one, and then they can go access anything they want on
> the old RPMB device (assuming the key hasn't changed; and if it has
> changed that's conversely a recipe for data loss if something goes wrong).
>
> I really think trying to automate any kind of "default" usage of an RPMB
> is a terrible idea. It needs to be a conscious decision on a
> per-platform basis.
>

Agree and via DT method I only meant to assign already provisioned
RPMB device/s either to TEE or Linux kernel. And RPMB key provisioning
being a one time process should be carried out carefully during device
manufacturing only.

-Sumit

> --
> Hector Martin (marcan@marcan.st)
> Public Key: https://mrcn.st/pub

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-10  9:48                 ` Linus Walleij
@ 2021-03-10 13:52                   ` Hector Martin
  2021-03-11  0:36                     ` Linus Walleij
  0 siblings, 1 reply; 47+ messages in thread
From: Hector Martin @ 2021-03-10 13:52 UTC (permalink / raw)
  To: Linus Walleij, David Howells, keyrings, Jarkko Sakkinen
  Cc: Sumit Garg, Arnd Bergmann, Joakim Bech, Alex Bennée,
	linux-kernel, Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta,
	Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Arnd Bergmann

On 10/03/2021 18.48, Linus Walleij wrote:
> Disk is encrypted, and RPMB is there to block any exhaustive
> password or other authentication token search.

This relies on having a secure boot chain to start with (otherwise you 
can just bypass policy that way; the RPMB is merely storage to give you 
anti-rollback properties, it can't enforce anything itself). So you 
would have to have a laptop with a fully locked down secure boot, which 
can only boot some version of Linux signed by you until, say, LUKS 
decryption. And then the tooling around that needs to be integrated with 
RPMB, to use it as an attempt counter.

But now this ends up having to involve userspace anyway; the kernel key 
stuff doesn't support policy like this, does it? So having the kernel 
automagically use RPMB wouldn't get us there.

I may be wrong on the details here, but as far as I know RPMB is 
strictly equivalent to a simple secure increment-only counter in what it 
buys you. The stuff about writing data to it securely is all a red 
herring - you can implement secure storage elsewhere, and with secure 
storage + a single secure counter, you can implement anti-rollback.

It is not intended to store keys in a way that is somehow safer than 
other mechanisms. After all, you need to securely store the RPMB key to 
begin with; you might as well use that to encrypt a keystore on any 
random block device.

> Ideally: the only way to make use of the hardware again would
> be to solder off the eMMC, if eMMC is used for RPMB.
> If we have RPMB on an NVME or UFS drive, the idea is
> to lock that thing such that it becomes useless and need to
> be replaced with a new part in this scenario.
> 
> In practice: make it hard, because we know no such jail is
> perfect. Make it not worth the effort, make it cheaper for thieves
> to just buy a new harddrive to use a stolen laptop, locking
> the data that was in it away forever by making the drive
> useless for any practical attacks.

But RPMB does not enforce any of this policy for you. RPMB only gives 
you a primitive: the ability to have storage that cannot be externally 
rolled back. So none of this works unless the entire system is set up to 
securely boot all the way until the drive unlock happens, and there are 
no other blatant code execution avenues.

There isn't even any encryption involved in the protocol, so all the 
data stored in the RPMB is public and available to any attacker.

So unless the kernel grows a subsystem/feature to enforce complex key 
policies (with things like use counts, retry times, etc), I don't think 
there's a place to integrate RPMB kernel-side. You still need a trusted 
userspace tool to glue it all together.

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub

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

* Re: [RFC PATCH  0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend
  2021-03-09 13:27 ` [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend Avri Altman
@ 2021-03-10 14:29   ` Alex Bennée
  2021-03-11 13:45     ` Avri Altman
  0 siblings, 1 reply; 47+ messages in thread
From: Alex Bennée @ 2021-03-10 14:29 UTC (permalink / raw)
  To: Avri Altman
  Cc: Matti.Moell, arnd, bing.zhu, hmo, ilias.apalodimas, joakim.bech,
	linux-kernel, linux-mmc, linux-nvme, linux-scsi, maxim.uvarov,
	ruchika.gupta, tomas.winkler, yang.huang


Avri Altman <avri.altman@wdc.com> writes:

> The mmc driver has some hooks to support rpmb access, but access is
> mainly facilitated from user space, e.g. mmc-utils.
>
> The ufs driver has no concept of rpmb access - it is facilitated via
> user space, e.g. ufs-utils and similar.
>
> Both for ufs and mmc, rpmb access is defined in their applicable jedec
> specs. This is the case for few years now - AFAIK No new rpmb-related
> stuff is expected to be introduced in the near future.
>
> What problems, as far as mmc and ufs, are you trying to solve by this
> new subsystem?

Well in my case the addition of virtio-rpmb. As yet another RPMB device
which only supports RPMB transactions and isn't part of a wider block
device. The API dissonance comes into play when looking to implement it
as part of wider block device stacks and then having to do things like
fake 0 length eMMC devices with just an RPMB partition to use existing
tools.

I guess that was the original attraction of having a common kernel
subsystem to interact with RPMB functionality regardless of the
underlying HW. However from the other comments it seems the preference
is just to leave it to user-space and domain specific tools for each
device type.

>
> Thanks,
> Avri


-- 
Alex Bennée

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-10 13:52                   ` Hector Martin
@ 2021-03-11  0:36                     ` Linus Walleij
  2021-03-11  9:22                       ` Hector Martin
  0 siblings, 1 reply; 47+ messages in thread
From: Linus Walleij @ 2021-03-11  0:36 UTC (permalink / raw)
  To: Hector Martin
  Cc: David Howells, keyrings, Jarkko Sakkinen, Sumit Garg,
	Arnd Bergmann, Joakim Bech, Alex Bennée, linux-kernel,
	Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta, Winkler, Tomas,
	yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc, linux-scsi,
	linux-nvme, Ulf Hansson, Arnd Bergmann

On Wed, Mar 10, 2021 at 2:52 PM Hector Martin <marcan@marcan.st> wrote:

> This relies on having a secure boot chain to start with (otherwise you
> can just bypass policy that way; the RPMB is merely storage to give you
> anti-rollback properties, it can't enforce anything itself). So you
> would have to have a laptop with a fully locked down secure boot, which
> can only boot some version of Linux signed by you until, say, LUKS
> decryption. And then the tooling around that needs to be integrated with
> RPMB, to use it as an attempt counter.

Yes and no. For secure boot yes. For other use cases it can
still be useful.

The way I understand it, there are people (not me) with secure boot
ambitions but I wouldn't say that is the only use case, see below.

> But now this ends up having to involve userspace anyway; the kernel key
> stuff doesn't support policy like this, does it? So having the kernel
> automagically use RPMB wouldn't get us there.

Yes, you are right, I had the wrong idea. It needs to be something
the user (or the users agent such as an organization) decides to
make use of, and it is policy so it should be in userspace. We
may standardize the key format on the device though.

> I may be wrong on the details here, but as far as I know RPMB is
> strictly equivalent to a simple secure increment-only counter in what it
> buys you. The stuff about writing data to it securely is all a red
> herring - you can implement secure storage elsewhere, and with secure
> storage + a single secure counter, you can implement anti-rollback.
>
> It is not intended to store keys in a way that is somehow safer than
> other mechanisms. After all, you need to securely store the RPMB key to
> begin with; you might as well use that to encrypt a keystore on any
> random block device.

The typical use-case mentioned in one reference is to restrict
the number of password/pin attempts and  combine that with
secure time to make sure that longer and longer intervals are
required between password attempts.

This seems pretty neat to me.

> But RPMB does not enforce any of this policy for you. RPMB only gives
> you a primitive: the ability to have storage that cannot be externally
> rolled back. So none of this works unless the entire system is set up to
> securely boot all the way until the drive unlock happens, and there are
> no other blatant code execution avenues.

This is true for firmware anti-rollback or say secure boot.

But RPMB can also be used for example for restricting the
number of PIN attempts.

A typical attack vector on phones (I think candybar phones
even) was a robot that was punching PIN codes to unlock
the phone, combined with an electronic probe that would
cut the WE (write enable) signal to the flash right after
punching a code. The counter was stored in the flash.

(A bit silly example as this can be countered by reading back
the counter from flash and checking etc, but you get the idea,
various versions of this attack is possible,)

With RPMB this can be properly protected against because
the next attempt can not be made until after the RPMB
monotonic counter has been increased.

Of course the system can be compromised in other ways,
(like, maybe it doesn't even have secure boot or even
no encrypted drive) but this is one of the protection
mechanisms that can plug one hole.

It is thus a countermeasure to keyboard emulators and other
evil hardware trying to brute force their way past screen
locks and passwords. Such devices exist, sadly.

> There isn't even any encryption involved in the protocol, so all the
> data stored in the RPMB is public and available to any attacker.

You need to pass the symmetric key to increase a counter
and store a new "key" (data chunk, 256 bytes). But as you say
one can read it without the symmetric key.

AFAICT that is not a problem for any use case for RPMB.

> So unless the kernel grows a subsystem/feature to enforce complex key
> policies (with things like use counts, retry times, etc), I don't think
> there's a place to integrate RPMB kernel-side. You still need a trusted
> userspace tool to glue it all together.

Yes, I understand such ambitions may exist and pretty much why
I CC the keyring people. So the question is whether they think
that is something they would go and use for e.g. passphrase
retries.

Yours,
Linus Walleij

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-10 10:29                 ` Sumit Garg
@ 2021-03-11  0:49                   ` Linus Walleij
  2021-03-11  1:07                     ` James Bottomley
  2021-03-11  9:45                     ` Hector Martin
  0 siblings, 2 replies; 47+ messages in thread
From: Linus Walleij @ 2021-03-11  0:49 UTC (permalink / raw)
  To: Sumit Garg
  Cc: Hector Martin, Arnd Bergmann, open list:ASYMMETRIC KEYS,
	David Howells, Jarkko Sakkinen, Joakim Bech, Alex Bennée,
	linux-kernel, Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta,
	Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Arnd Bergmann

On Wed, Mar 10, 2021 at 11:29 AM Sumit Garg <sumit.garg@linaro.org> wrote:

> And RPMB key provisioning
> being a one time process should be carried out carefully during device
> manufacturing only.

For a product use case such as a mobile or chromebook or
set-top box: yes. In this scenario something like TEE possesses
this symmetric key.

But for a random laptop with an NVME containing an RPMB it
may be something the user want to initialize and use to lock down
their machine.

The use case for TPM on laptops is similar: it can be used by a
provider to lock down a machine, but it can also be used by the
random user to store keys. Very few users beside James
Bottomley are capable of doing that (I am not) but they exist.
https://blog.hansenpartnership.com/using-your-tpm-as-a-secure-key-store/

I think we need to think not only of existing use cases but also
possible ones even if there is currently no software for other
use cases. (But maybe that is too ambitious.)

Yours,
Linus Walleij

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-11  0:49                   ` Linus Walleij
@ 2021-03-11  1:07                     ` James Bottomley
  2021-03-11  9:45                     ` Hector Martin
  1 sibling, 0 replies; 47+ messages in thread
From: James Bottomley @ 2021-03-11  1:07 UTC (permalink / raw)
  To: Linus Walleij, Sumit Garg
  Cc: Hector Martin, Arnd Bergmann, open list:ASYMMETRIC KEYS,
	David Howells, Jarkko Sakkinen, Joakim Bech, Alex Bennée,
	linux-kernel, Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta,
	Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Arnd Bergmann

On Thu, 2021-03-11 at 01:49 +0100, Linus Walleij wrote:
> The use case for TPM on laptops is similar: it can be used by a
> provider to lock down a machine, but it can also be used by the
> random user to store keys. Very few users beside James
> Bottomley are capable of doing that (I am not)

Yes, that's the problem with the TPM: pretty much no-one other than
someone prepared to become an expert in the subject can use it.  This
means that enabling RPMB is unlikely to be useful ... you have to
develop easy use cases for it as well.

>  but they exist.
> https://blog.hansenpartnership.com/using-your-tpm-as-a-secure-key-store/

It's the difficulty of actually *using* the thing as a keystore which
causes the problem.   The trick to expanding use it to make it simple.
Not to derail the thread, but this should hopefully become a whole lot
easier soon.  Gnupg-2.3 will release with easy to use TPM support for
all your gpg keys:

https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=log;h=6720f1343aef9342127380b155c19e12c92d65ac

It's not the end of the road by any means, but hopefully it will become
a beach head of sorts for more uses.

James



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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-11  0:36                     ` Linus Walleij
@ 2021-03-11  9:22                       ` Hector Martin
  2021-03-11 14:06                         ` Linus Walleij
  0 siblings, 1 reply; 47+ messages in thread
From: Hector Martin @ 2021-03-11  9:22 UTC (permalink / raw)
  To: Linus Walleij
  Cc: David Howells, keyrings, Jarkko Sakkinen, Sumit Garg,
	Arnd Bergmann, Joakim Bech, Alex Bennée, linux-kernel,
	Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta, Winkler, Tomas,
	yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc, linux-scsi,
	linux-nvme, Ulf Hansson, Arnd Bergmann

On 11/03/2021 09.36, Linus Walleij wrote:
>> It is not intended to store keys in a way that is somehow safer than
>> other mechanisms. After all, you need to securely store the RPMB key to
>> begin with; you might as well use that to encrypt a keystore on any
>> random block device.
> 
> The typical use-case mentioned in one reference is to restrict
> the number of password/pin attempts and  combine that with
> secure time to make sure that longer and longer intervals are
> required between password attempts.
> 
> This seems pretty neat to me.

Yes, but to implement that you don't need any secure storage *at all*. 
If all the RPMB did was authenticate an incrementing counter, you could 
just store the <last timestamp, attempts remaining> tuple inside a blob 
of secure (encrypted and MACed) storage on any random Flash device, 
along with the counter value, and thus prevent rollbacks that way (some 
finer design points are needed to deal with power loss protection and 
ordering, but the theory holds).

Basically what I'm saying is that for security *guarantee* purposes, 
AFAICT the storage part of RPMB makes no difference. It is useful in 
practical implementations for various reasons, but if you think you can 
use that secure storage to provide security properties which you 
couldn't do otherwise, you are probably being misled. If you're trying 
to understand what having RPMB gets you over not having it, it helps if 
you ignore all the storage stuff and just view it as a single secure, 
increment-only counter.

> 
>> But RPMB does not enforce any of this policy for you. RPMB only gives
>> you a primitive: the ability to have storage that cannot be externally
>> rolled back. So none of this works unless the entire system is set up to
>> securely boot all the way until the drive unlock happens, and there are
>> no other blatant code execution avenues.
> 
> This is true for firmware anti-rollback or say secure boot.
> 
> But RPMB can also be used for example for restricting the
> number of PIN attempts.
> 
> A typical attack vector on phones (I think candybar phones
> even) was a robot that was punching PIN codes to unlock
> the phone, combined with an electronic probe that would
> cut the WE (write enable) signal to the flash right after
> punching a code. The counter was stored in the flash.
> 
> (A bit silly example as this can be countered by reading back
> the counter from flash and checking etc, but you get the idea,
> various versions of this attack is possible,)
> 
> With RPMB this can be properly protected against because
> the next attempt can not be made until after the RPMB
> monotonic counter has been increased.

But this is only enforced by software. If you do not have secure boot, 
you can just patch software to allow infinite tries without touching the 
RPMB. The RPMB doesn't check PINs for you, it doesn't even gate read 
access to data in any way. All it does is promise you cannot make the 
counter count down, or make the data stored within go back in time.

> Of course the system can be compromised in other ways,
> (like, maybe it doesn't even have secure boot or even
> no encrypted drive) but this is one of the protection
> mechanisms that can plug one hole.

This is hot how security systems are designed though; you do not "plug 
holes", what you do is cover more attack scenarios, and you do that in 
the order from simplest to hardest.

If we are trying to crack the PIN on a device we have physical access 
to, the simplest and most effective attack is to just run your own 
software on the machine, extract whatever hash or material you need to 
validate PINs, and do it offline.

To protect against that, you first need to move the PIN checking into a 
trust domain where an attacker with physical access can't easily break 
in, which means secure boot.

*Then* the next simplest attack is a secure storage rollback attack, 
which is what I described in that blog post about iOS. And *now* it 
makes sense to start thinking about the RPMB.

But RPMB alone doesn't make any sense on a system without secure boot. 
It doesn't change anything; in both cases the simplest attack is to just 
run your own software.

> It is thus a countermeasure to keyboard emulators and other
> evil hardware trying to brute force their way past screen
> locks and passwords. Such devices exist, sadly.

If you're trying to protect against a "dumb" attack with a keyboard 
emulator that doesn't consider access to physical storage, then you 
don't need RPMB either; you can just put the PIN unlock counter in a 
random file.

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-11  0:49                   ` Linus Walleij
  2021-03-11  1:07                     ` James Bottomley
@ 2021-03-11  9:45                     ` Hector Martin
  2021-03-11 14:31                       ` Linus Walleij
  1 sibling, 1 reply; 47+ messages in thread
From: Hector Martin @ 2021-03-11  9:45 UTC (permalink / raw)
  To: Linus Walleij, Sumit Garg
  Cc: Arnd Bergmann, open list:ASYMMETRIC KEYS, David Howells,
	Jarkko Sakkinen, Joakim Bech, Alex Bennée, linux-kernel,
	Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta, Winkler, Tomas,
	yang.huang, bing.zhu, Matti.Moell, hmo, linux-mmc, linux-scsi,
	linux-nvme, Ulf Hansson, Arnd Bergmann

On 11/03/2021 09.49, Linus Walleij wrote:
> The use case for TPM on laptops is similar: it can be used by a
> provider to lock down a machine, but it can also be used by the
> random user to store keys. Very few users beside James
> Bottomley are capable of doing that (I am not) but they exist.
> https://blog.hansenpartnership.com/using-your-tpm-as-a-secure-key-store/

I've used a TPM as an SSH key keystore in the past (these days I use 
YubiKeys, but same idea). TPMs are useful because they *do* implement 
policy and cryptographic operations. So you can, in fact, get security 
guarantees out of a TPM without secureboot.

For example, assuming the TPM is secure, it is impossible to clone an 
SSH key private key managed by a TPM. This means that any usage has to 
be on-device, which provides inherent rate-limiting. Then, the TPM can 
gate access to the key based on a passphrase, which again provides 
inherent rate-limits on cracking attempts. TPM 2.0 devices also provide 
explicit count limits and time-based throttling for unlocking attempts.

We have much the same story with the Secure Enclave Processor on Apple 
Silicon machines (which I'm working on porting Linux to) - it provides 
policy, and can even authenticate with fingerprints (there is a hardware 
secure channel between the reader and the SEP) as well as passphrases. 
For all intents and purposes it is an Apple-managed TPM (with its own 
secureboot). So it is similarly useful for us to support the SEP for key 
storage, and perhaps even integrate it with kernel subsystems at some 
point. It's useful for our regular users, even though they are unlikely 
to be running with full secureboot on the main CPU (though Apple's 
implementation does allow for a user-controlled secureboot subset, and 
it should be possible to provide hard guarantees there as well, but I 
digress).

All of these things make putting keys into TPMs, YubiKeys, the SEP, etc 
a useful thing for anyone, regardless of whether their machine is locked 
down or not.

This is not the case for RPMB. RPMB *relies* on the software running on 
the other side being trusted. RPMB, alone, provides zero new security 
guarantees, without trusted software communicating with it.

The key initialization story is also a lot thornier in RPMB. TPMs, the 
SEP, and YubiKeys are all designed so that they can be factory-reset 
(losing all key material in the process) by a user with physical access, 
which means that provisioning operations and experiments are risk-free, 
and the only danger is data loss, not making the hardware less useful. 
With the MAC key provisioning for RPMB being a one-time process, it is 
inherently a very risky operation that a user must commit to with great 
care, as they only get one chance, ever. Better have that key backed up 
somewhere (but not somewhere an attacker can get to... see the 
problem?). This is like fusing secureboot keys on SoCs (I remember being 
*very* nervous about hitting <enter> on the command to fuse a Tegra X1 
board with a secureboot key for some experiments... these kinds of 
irreversible things are no joke).

Basically, TPMs, SEP, YubiKeys, etc were designed to be generally useful 
and flexible devices for various crypto and authentication use cases. 
RPMB was designed for the sole purpose of plugging the secure storage 
replay exploit for Android phones running TrustZone secure monitors. It 
doesn't really do anything else; it's just a single low-level primitive 
and you need to already have an equivalent design that is only missing 
that piece to get anything from it. And its provisioning model assumes a 
typical OEM device production pipeline and integration with CPU fusing; 
it isn't friendly to Linux hackers messing around with securing LUKS 
unlock attempt counters.

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub

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

* RE: [RFC PATCH  0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend
  2021-03-10 14:29   ` Alex Bennée
@ 2021-03-11 13:45     ` Avri Altman
  0 siblings, 0 replies; 47+ messages in thread
From: Avri Altman @ 2021-03-11 13:45 UTC (permalink / raw)
  To: Alex Bennée
  Cc: Matti.Moell, arnd, bing.zhu, hmo, ilias.apalodimas, joakim.bech,
	linux-kernel, linux-mmc, linux-nvme, linux-scsi, maxim.uvarov,
	ruchika.gupta, tomas.winkler, yang.huang

> Avri Altman <avri.altman@wdc.com> writes:
> 
> > The mmc driver has some hooks to support rpmb access, but access is
> > mainly facilitated from user space, e.g. mmc-utils.
> >
> > The ufs driver has no concept of rpmb access - it is facilitated via
> > user space, e.g. ufs-utils and similar.
> >
> > Both for ufs and mmc, rpmb access is defined in their applicable jedec
> > specs. This is the case for few years now - AFAIK No new rpmb-related
> > stuff is expected to be introduced in the near future.
> >
> > What problems, as far as mmc and ufs, are you trying to solve by this
> > new subsystem?
> 
> Well in my case the addition of virtio-rpmb. As yet another RPMB device
> which only supports RPMB transactions and isn't part of a wider block
> device. The API dissonance comes into play when looking to implement it
> as part of wider block device stacks and then having to do things like
> fake 0 length eMMC devices with just an RPMB partition to use existing
> tools.
> 
> I guess that was the original attraction of having a common kernel
> subsystem to interact with RPMB functionality regardless of the
> underlying HW. However from the other comments it seems the preference
> is just to leave it to user-space and domain specific tools for each
> device type.
Yes.  It took us a while to clean those tools to perform by-spec rpmb access.

Anyway, I do see value in Tomas's/your approach, 
that will refrain from the need to register a designated block device.
Provided that each host is allowed to register its own ops.
Those ops can be a super-set of the various device types.
For ufs and mmc rpmb ops contains 7 operations.

Thanks,
Avri

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-11  9:22                       ` Hector Martin
@ 2021-03-11 14:06                         ` Linus Walleij
  2021-03-11 20:02                           ` Hector Martin
  0 siblings, 1 reply; 47+ messages in thread
From: Linus Walleij @ 2021-03-11 14:06 UTC (permalink / raw)
  To: Hector Martin
  Cc: David Howells, open list:ASYMMETRIC KEYS, Jarkko Sakkinen,
	Sumit Garg, Arnd Bergmann, Joakim Bech, Alex Bennée,
	linux-kernel, Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta,
	Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Arnd Bergmann

On Thu, Mar 11, 2021 at 10:22 AM Hector Martin <marcan@marcan.st> wrote:
> On 11/03/2021 09.36, Linus Walleij wrote:

> > The typical use-case mentioned in one reference is to restrict
> > the number of password/pin attempts and  combine that with
> > secure time to make sure that longer and longer intervals are
> > required between password attempts.
> >
> > This seems pretty neat to me.
>
> Yes, but to implement that you don't need any secure storage *at all*.
> If all the RPMB did was authenticate an incrementing counter, you could
> just store the <last timestamp, attempts remaining> tuple inside a blob
> of secure (encrypted and MACed) storage on any random Flash device,
> along with the counter value, and thus prevent rollbacks that way (some
> finer design points are needed to deal with power loss protection and
> ordering, but the theory holds).

Yes. And this is what mobile phone vendors typically did.

But the nature of different electrical attacks made them worried
about different schemes involving cutting power and disturbing
signals with different probes, so they wanted this counter
implemented in hardware and that is why RPMB exists at all
(IIUC).

It is fine to be of the opinion that this entire piece of hardware
is pointless because the same can be achieved using
well written software.

The position that the kernel community shall just ignore this
hardware is a possible outcome of this discussion, but we need
to have the discussion anyway, because now a RPMB framework
is being promoted. The people who want it will need to sell it to
us.

> > With RPMB this can be properly protected against because
> > the next attempt can not be made until after the RPMB
> > monotonic counter has been increased.
>
> But this is only enforced by software. If you do not have secure boot,
> you can just patch software to allow infinite tries without touching the
> RPMB. The RPMB doesn't check PINs for you, it doesn't even gate read
> access to data in any way. All it does is promise you cannot make the
> counter count down, or make the data stored within go back in time.

This is true, I guess the argument is something along the
line that if one link in the chain is weaker, why harden
any other link, the chain will break anyway?

(The rest of your message seems to underscore this
position.)

I am more of the position let's harden this link if we can
and then deal with the others when they come up, i.e.
my concern is this piece of the puzzle, even if it is not
the centerpiece (maybe the centerpiece is secure boot
what do I know).

Yours,
Linus Walleij

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-11  9:45                     ` Hector Martin
@ 2021-03-11 14:31                       ` Linus Walleij
  2021-03-11 20:29                         ` Hector Martin
  0 siblings, 1 reply; 47+ messages in thread
From: Linus Walleij @ 2021-03-11 14:31 UTC (permalink / raw)
  To: Hector Martin
  Cc: Sumit Garg, Arnd Bergmann, open list:ASYMMETRIC KEYS,
	David Howells, Jarkko Sakkinen, Joakim Bech, Alex Bennée,
	linux-kernel, Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta,
	Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Arnd Bergmann

Hi Marcan,

thanks for the detailed description of your experience with the TPM and
secure enclave! This is the kind of thinking and experience we really
need here because it paints the bigger picture.

I am very happy for involving you in this discussion because of your
wide perspective on these features.

On Thu, Mar 11, 2021 at 10:45 AM Hector Martin <marcan@marcan.st> wrote:

> All of these things make putting keys into TPMs, YubiKeys, the SEP, etc
> a useful thing for anyone, regardless of whether their machine is locked
> down or not.
>
> This is not the case for RPMB. RPMB *relies* on the software running on
> the other side being trusted. RPMB, alone, provides zero new security
> guarantees, without trusted software communicating with it.

I kind of agree.

My position is more like this: different storage media like eMMC,
nvme etc are starting to provide RPMB, so we should provide an
interface to it, harden it and test it, such that trusted systems can
eventually use them, once they get there.

eMMC and NVME already have divergent RPMB userspace
interfaces. The code is already there. An in-kernel interface
and joining the interfaces is under discussion. ($SUBJECT)

Currently engineers are probably concerned with being able to
make use of RPMB in their machines for present day TEE use
cases, but as community we need to think of a future scenario
where we may want to use it, because the abstractions are being
added now, it seems. (Otherwise, in the future, someone is
going to say: "why didn't you think about that from the beginning?")

It's a fine line. Sometimes it becomes just immature up-front
design. Luckily we have people like you telling us off ;)

> With the MAC key provisioning for RPMB being a one-time process, it is
> inherently a very risky operation that a user must commit to with great
> care, as they only get one chance, ever.

Yes.

Current use cases involve that key mostly being set in manufacturing
by vendors and accessible to a TEE-like secure world. It fits
them. Their expectation is that the secure world is managed
by them and tightly connected to the root of trust in the machine.

Then we have these random devices which just happen to
have some RPMB on them, sitting around for no reason.
The software such as a Linux distribution has not figured
out a use case.

As a developer, dark silicon is disturbing to me so I try to think
about a use case.

My idea is more like a one-time use seal: the first user of the
machine can use this RPMB store to get some hardware backing
for rollback, pin attempts etc, but once that user is done
with the machine you have no RPMB anymore (unless the
user gives the key to the next user).

If they just reformat the harddrive and lose the key, the ability
to use this hardware is forever gone.

Then software will cope with the situation. Such things
happen.

It is uncomfortable for those of us coming from the world of
generic computing to think about hardware resources as
one-time-use-only but in other strands of life such as medical
needles this is not unheard of, it happens.

> RPMB was designed for the sole purpose of plugging the secure storage
> replay exploit for Android phones running TrustZone secure monitors. It
> doesn't really do anything else; it's just a single low-level primitive
> and you need to already have an equivalent design that is only missing
> that piece to get anything from it. And its provisioning model assumes a
> typical OEM device production pipeline and integration with CPU fusing;
> it isn't friendly to Linux hackers messing around with securing LUKS
> unlock attempt counters.

I understand your argument, is your position such that the nature
of the hardware is such that community should leave this hardware
alone and not try to make use of RPMB  for say ordinary (self-installed)
Linux distributions?

Yours,
Linus Walleij

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-11 14:06                         ` Linus Walleij
@ 2021-03-11 20:02                           ` Hector Martin
  2021-03-12  9:22                             ` Linus Walleij
  0 siblings, 1 reply; 47+ messages in thread
From: Hector Martin @ 2021-03-11 20:02 UTC (permalink / raw)
  To: Linus Walleij
  Cc: David Howells, open list:ASYMMETRIC KEYS, Jarkko Sakkinen,
	Sumit Garg, Arnd Bergmann, Joakim Bech, Alex Bennée,
	linux-kernel, Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta,
	Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Arnd Bergmann

On 11/03/2021 23.06, Linus Walleij wrote:
> Yes. And this is what mobile phone vendors typically did.
> 
> But the nature of different electrical attacks made them worried
> about different schemes involving cutting power and disturbing
> signals with different probes, so they wanted this counter
> implemented in hardware and that is why RPMB exists at all
> (IIUC).

No, prior to RPMB there was no such secure counter at all. The problem 
is that non-volatile erasable storage (i.e. EEPROM/Flash) is 
incompatible with modern SoC manufacturing processes, so there is no way 
to embed a secure counter into the main SoC. And once your counter needs 
to be external, there needs to be a secure communications protocol to 
access it. This is what RPMB implements.

For preventing software downgrades, especially of bootloader code, this 
can be implemented with one-time fuses embedded in the SoC, but there is 
a limited supply of those. So this doesn't work for things like PIN 
attempt counters. For that you need a secure external counter.

> It is fine to be of the opinion that this entire piece of hardware
> is pointless because the same can be achieved using
> well written software.

You've misunderstood me. RPMB isn't pointless; what I am saying is that 
if you strip away everything but the counter functionality, you can 
still build equivalent security guarantees. You still need the counter. 
There is no way to get that counter without RPMB or something like it 
(another option is e.g. to use a smartcard IC as a secure element; AIUI 
modern Apple devices do this). Software alone doesn't work. This is why 
I wrote that article about how the FBI cracks iPhones; that works 
because they weren't using a secure rollback-protected storage/counter 
chip of any kind.

> The position that the kernel community shall just ignore this
> hardware is a possible outcome of this discussion, but we need
> to have the discussion anyway, because now a RPMB framework
> is being promoted. The people who want it will need to sell it to
> us.

Again, you're kind of misunderstanding me here. I'm not saying the 
feature is useless. What I'm saying is that, to understand *how* it is 
useful, it helps if you forget about the read/write commands and treat 
it as a simple counter.

Once you do that, you'll realize that e.g. putting keys in RPMB doesn't 
really make sense as a kernel primitive. The usefulness of RPMB is 
purely in the integration of that counter (which is equivalent to 
rollback-protected storage) with a policy system. Everything else is 
icing on the cake; it doesn't create new use cases.

Consider this:

* You have RPMB, but you will use it as a counter only.
* To use RPMB, you need to have a secure shared key.
* You use the RPMB key (or a hash, or whatever) to encrypt a GPG key in 
your filesystem
* You have a Git repo. This is your secure rollback-protected storage.
* We assume the filesystem can be potentially read, written, and 
intercepted.

To read from your rollback-protected storage, you:

* Read the RPMB counter securely
* Fetch the Git tag named "v%d" with the counter value
* Ensure the Git tag is correctly signed with your secure GPG key
* Ensure the commit description of the signed commit is also "v%d"

To write to your rollback protected storage, you:

* Commit your changes to the repository (as a child of the current known 
good commit, which you know is secure via the prevous read process) with 
the commit message "v%d" with the counter value + 1
* Tag it "v%d" with the current counter value + 1, signing the tag with 
your GPG private key
* Ensure all changes are flushed to disk
* Perform an increment operation on the RPMB counter

You have now built a secure, rollback-protected Git repository, with 
similar security properties to RPMB storage, without using RPMB storage; 
just a counter.

Just like RPMB storage, the repo is readable without a key.
Just like RPMB storage, you need to have a secured master key stored 
somewhere; if the attacker gets your key it is game over.
Just like RPMB storage, the repo can only be updated (in a way that 
would be accepted) with the key available
Just like RPMB storage, the repo cannot be rolled back to a prior 
version (because it would not match the counter value)

Thus, we can conclude that the storage features of RPMB do not provide 
additional security properties that cannot be derived from a simple counter.

* Disclaimer: please don't actually deploy this; I'm trying to make a 
point here, it's 5AM and I'm not claiming this is a perfectly secure 
design and I haven't missed anything. Please don't design 
rollback-protected Git repositories without expert review. I am assuming 
filesystem mutations only happen between operations and handwaving away 
active attacks, which I doubt Git is designed to be robust against. A 
scheme like this can be implemented securely with care, but not naively.

>>> With RPMB this can be properly protected against because
>>> the next attempt can not be made until after the RPMB
>>> monotonic counter has been increased.
>>
>> But this is only enforced by software. If you do not have secure boot,
>> you can just patch software to allow infinite tries without touching the
>> RPMB. The RPMB doesn't check PINs for you, it doesn't even gate read
>> access to data in any way. All it does is promise you cannot make the
>> counter count down, or make the data stored within go back in time.
> 
> This is true, I guess the argument is something along the
> line that if one link in the chain is weaker, why harden
> any other link, the chain will break anyway?

This is how security works, yes :-)

I'm not saying hardening a link in the chain is pointless in every case, 
but in this case it's like heat treating one link in the chain, then 
joining it to the next one with a ziptie. Only once you at least have 
the entire chain of steel does it make sense to start thinking about 
heat treatment.

> I am more of the position let's harden this link if we can
> and then deal with the others when they come up, i.e.
> my concern is this piece of the puzzle, even if it is not
> the centerpiece (maybe the centerpiece is secure boot
> what do I know).

Well, that's what I'm saying, you do need secureboot for this to make 
sense :-)

RPMB isn't useless and some systems should implement it; but there's no 
real way for the kernel to transparently use it to improve security in 
general (for anyone) without the user being aware. Since any security 
benefit from RPMB must come from integration with user policy, it 
doesn't make sense to "well, just do something else with RPMB because 
it's better than nothing"; just doing "something" doesn't make systems 
more secure. There needs to be a specific, practical use case that we'd 
be trying to solve with RPMB here.

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-11 14:31                       ` Linus Walleij
@ 2021-03-11 20:29                         ` Hector Martin
  2021-03-11 20:57                           ` Alex Bennée
                                             ` (2 more replies)
  0 siblings, 3 replies; 47+ messages in thread
From: Hector Martin @ 2021-03-11 20:29 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Sumit Garg, Arnd Bergmann, open list:ASYMMETRIC KEYS,
	David Howells, Jarkko Sakkinen, Joakim Bech, Alex Bennée,
	linux-kernel, Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta,
	Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Arnd Bergmann

On 11/03/2021 23.31, Linus Walleij wrote:
> I understand your argument, is your position such that the nature
> of the hardware is such that community should leave this hardware
> alone and not try to make use of RPMB  for say ordinary (self-installed)
> Linux distributions?

It's not really that the community should leave this hardware alone, so 
much that I think there is a very small subset of users who will be able 
to benefit from it, and that subset will be happy with a usable 
kernel/userspace interface and some userspace tooling for this purpose, 
including provisioning and such.

Consider the prerequisites for using RPMB usefully here:

* You need (user-controlled) secureboot
* You need secret key storage - so either some kind of CPU-fused key, or 
one protected by a TPM paired with the secureboot (key sealed to PCR 
values and such)
* But if you have a TPM, that can handle secure counters for you already 
AIUI, so you don't need RPMB
* So this means you must be running a non-TPM secureboot system

And so we're back to embedded platforms like Android phones and other 
SoC stuff... user-controlled secureboot is already somewhat rare here, 
and even rarer are the cases where the user controls the whole chain 
including the TEE if any (otherwise it'll be using RPMB already); this 
pretty much excludes all production Android phones except for a few 
designed as completely open systems; we're left with those and a subset 
of dev boards (e.g. the Jetson TX1 I did fuse experiments on). In the 
end, those systems will probably end up with fairly bespoke set-ups for 
any given device or SoC family, for using RPMB.

But then again, if you have a full secureboot system where you control 
the TEE level, wouldn't you want to put the RPMB shenanigans there and 
get some semblance of secure TPM/keystore/attempt throttling 
functionality that is robust against Linux exploits and has a smaller 
attack surface? Systems without EL3 are rare (Apple M1 :-)) so it makes 
more sense to do this on those that do have it. If you're paranoid 
enough to be getting into building your own secure system with 
anti-rollback for retry counters, you should be heading in that directly 
anyway.

And now Linux's RPMB code is useless because you're running the stack in 
the secure monitor instead :-)

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-11 20:29                         ` Hector Martin
@ 2021-03-11 20:57                           ` Alex Bennée
  2021-03-12 10:00                             ` Linus Walleij
  2021-03-12  9:47                           ` Linus Walleij
  2021-03-12 11:59                           ` Sumit Garg
  2 siblings, 1 reply; 47+ messages in thread
From: Alex Bennée @ 2021-03-11 20:57 UTC (permalink / raw)
  To: Hector Martin
  Cc: Linus Walleij, Sumit Garg, Arnd Bergmann,
	open list:ASYMMETRIC KEYS, David Howells, Jarkko Sakkinen,
	Joakim Bech, linux-kernel, Maxim Uvarov, Ilias Apalodimas,
	Ruchika Gupta, Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell,
	hmo, linux-mmc, linux-scsi, Ulf Hansson, Arnd Bergmann


Hector Martin <marcan@marcan.st> writes:

> On 11/03/2021 23.31, Linus Walleij wrote:
>> I understand your argument, is your position such that the nature
>> of the hardware is such that community should leave this hardware
>> alone and not try to make use of RPMB  for say ordinary (self-installed)
>> Linux distributions?
>
> It's not really that the community should leave this hardware alone, so 
> much that I think there is a very small subset of users who will be able 
> to benefit from it, and that subset will be happy with a usable 
> kernel/userspace interface and some userspace tooling for this purpose, 
> including provisioning and such.
>
> Consider the prerequisites for using RPMB usefully here:
>
> * You need (user-controlled) secureboot
> * You need secret key storage - so either some kind of CPU-fused key, or 
> one protected by a TPM paired with the secureboot (key sealed to PCR 
> values and such)
> * But if you have a TPM, that can handle secure counters for you already 
> AIUI, so you don't need RPMB
> * So this means you must be running a non-TPM secureboot system
>
> And so we're back to embedded platforms like Android phones and other 
> SoC stuff... user-controlled secureboot is already somewhat rare here, 
> and even rarer are the cases where the user controls the whole chain 
> including the TEE if any (otherwise it'll be using RPMB already); this 
> pretty much excludes all production Android phones except for a few 
> designed as completely open systems; we're left with those and a subset 
> of dev boards (e.g. the Jetson TX1 I did fuse experiments on). In the 
> end, those systems will probably end up with fairly bespoke set-ups for 
> any given device or SoC family, for using RPMB.
>
> But then again, if you have a full secureboot system where you control 
> the TEE level, wouldn't you want to put the RPMB shenanigans there and 
> get some semblance of secure TPM/keystore/attempt throttling 
> functionality that is robust against Linux exploits and has a smaller 
> attack surface? Systems without EL3 are rare (Apple M1 :-)) so it makes 
> more sense to do this on those that do have it. If you're paranoid 
> enough to be getting into building your own secure system with 
> anti-rollback for retry counters, you should be heading in that directly 
> anyway.
>
> And now Linux's RPMB code is useless because you're running the stack in 
> the secure monitor instead :-)

Well quiet - the principle use-case of virtio-rpmb is to provide a RPMB
like device emulation for things like OPTEE when running under QEMU's
full-system emulation. However when it came to testing it out I went for
Thomas' patches because that was the only virtio FE implementation
available.

When I finished the implementation and Ilias started on the uBoot
front-end for virtio-rpmb. uBoot being firmware could very well be
running in the secure world so would have a valid use case accessing an
RPMB device. We ran into API dissonance because uboot's driver model is
roughly modelled on the kernel so expects to be talking to eMMC devices
which lead to requirements to fake something up to keep the driver
stacks happy.

I guess what we are saying is that real secure monitors should come up
with their own common API for interfacing with RPMB devices without
looking to the Linux kernel for inspiration?

-- 
Alex Bennée

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-11 20:02                           ` Hector Martin
@ 2021-03-12  9:22                             ` Linus Walleij
  0 siblings, 0 replies; 47+ messages in thread
From: Linus Walleij @ 2021-03-12  9:22 UTC (permalink / raw)
  To: Hector Martin
  Cc: David Howells, open list:ASYMMETRIC KEYS, Jarkko Sakkinen,
	Sumit Garg, Arnd Bergmann, Joakim Bech, Alex Bennée,
	linux-kernel, Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta,
	Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Arnd Bergmann

Hi Hector,

thanks for the long and detailed answer! I learn new things
all the time. (Maybe one day I add something too, who knows.)

I hope I'm not taking too much of your time, we're having fun :)

On Thu, Mar 11, 2021 at 9:02 PM Hector Martin <marcan@marcan.st> wrote:
> On 11/03/2021 23.06, Linus Walleij wrote:
> > Yes. And this is what mobile phone vendors typically did.
> >
> > But the nature of different electrical attacks made them worried
> > about different schemes involving cutting power and disturbing
> > signals with different probes, so they wanted this counter
> > implemented in hardware and that is why RPMB exists at all
> > (IIUC).
>
> No, prior to RPMB there was no such secure counter at all. The problem
> is that non-volatile erasable storage (i.e. EEPROM/Flash) is
> incompatible with modern SoC manufacturing processes, so there is no way
> to embed a secure counter into the main SoC. And once your counter needs
> to be external, there needs to be a secure communications protocol to
> access it. This is what RPMB implements.
>
> For preventing software downgrades, especially of bootloader code, this
> can be implemented with one-time fuses embedded in the SoC, but there is
> a limited supply of those. So this doesn't work for things like PIN
> attempt counters. For that you need a secure external counter.

Actually what we did (I was there, kind of) was to go to the flash vendors
(IIRC first Intel) and require what is today called "fuses" in the flash
memory.

Originally this was for things like unique serial numbers set in
production. But they could easily add some more of it for other
use cases.

This became what is known as OTP (one time programmable flash).
The OTP was all set to 1:s when the flash was new, then what we
did for anti-rollback was to designate some bits for software versions.

To make sure the OTP readout wasn't tampered with, some additional
hashes of the OTP was stored in the flash and MAC signed. This was
recalculated when we changed a bit from 1->0 in the OTP to indicate
a new firmware version.

Clever, isn't it? :)

I think the scheme in RPMB was based in part on the needs
solved by that crude mechanism.

(Linux MTD did actually even gain some support for OTP recently,
it is used only from userspace AFIAK.)

> RPMB isn't pointless; what I am saying is that
> if you strip away everything but the counter functionality, you can
> still build equivalent security guarantees. You still need the counter.
> There is no way to get that counter without RPMB or something like it
> (another option is e.g. to use a smartcard IC as a secure element; AIUI
> modern Apple devices do this). Software alone doesn't work. This is why
> I wrote that article about how the FBI cracks iPhones; that works
> because they weren't using a secure rollback-protected storage/counter
> chip of any kind.

Yeah. Hm, actually if they had flash memory they should have
used the OTP... But I suppose they were all on eMMC.

> it helps if you forget about the read/write commands and treat
> it as a simple counter.

Yep you're right.

> Once you do that, you'll realize that e.g. putting keys in RPMB doesn't
> really make sense as a kernel primitive. The usefulness of RPMB is
> purely in the integration of that counter (which is equivalent to
> rollback-protected storage) with a policy system. Everything else is
> icing on the cake; it doesn't create new use cases.

OK I understand. So what you're saying is we can't develop
anything without also developing a full policy system.

> Consider this:
(...)
> You have now built a secure, rollback-protected Git repository, with
> similar security properties to RPMB storage, without using RPMB storage;
> just a counter.

This example of using the RPMB to protect rollback of a git
was really nice! I think I understood as much before but
maybe I don't explain that well enough :/

> Thus, we can conclude that the storage features of RPMB do not provide
> additional security properties that cannot be derived from a simple counter.

I agree.

> * Disclaimer: please don't actually deploy this; I'm trying to make a
> point here, it's 5AM and I'm not claiming this is a perfectly secure
> design and I haven't missed anything. Please don't design
> rollback-protected Git repositories without expert review. I am assuming
> filesystem mutations only happen between operations and handwaving away
> active attacks, which I doubt Git is designed to be robust against. A
> scheme like this can be implemented securely with care, but not naively.

It's an example all kernel developers can relate to, so the
educational value is high!

> Well, that's what I'm saying, you do need secureboot for this to make
> sense :-)
>
> RPMB isn't useless and some systems should implement it; but there's no
> real way for the kernel to transparently use it to improve security in
> general (for anyone) without the user being aware. Since any security
> benefit from RPMB must come from integration with user policy, it
> doesn't make sense to "well, just do something else with RPMB because
> it's better than nothing"; just doing "something" doesn't make systems
> more secure. There needs to be a specific, practical use case that we'd
> be trying to solve with RPMB here.

As of now there are no other real world examples than TEE
for this user policy. TPM and secure enclave exist, but they both
have their own counters and does not need this.

Can one realistically imagine another secure environment
needing a RPMB counter? If not, then TEE (tee-supplicant is
the name of the software daemon in userspace for OP-TEE,
then some vendors have their own version of TEE)
will ever be the only user, and then we only need to design
for that.

Yours,
Linus Walleij

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-11 20:29                         ` Hector Martin
  2021-03-11 20:57                           ` Alex Bennée
@ 2021-03-12  9:47                           ` Linus Walleij
  2021-03-12 11:59                           ` Sumit Garg
  2 siblings, 0 replies; 47+ messages in thread
From: Linus Walleij @ 2021-03-12  9:47 UTC (permalink / raw)
  To: Hector Martin
  Cc: Sumit Garg, Arnd Bergmann, open list:ASYMMETRIC KEYS,
	David Howells, Jarkko Sakkinen, Joakim Bech, Alex Bennée,
	linux-kernel, Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta,
	Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Arnd Bergmann

Hi Hector,

I see a misunderstanding here :) explaining below.

On Thu, Mar 11, 2021 at 9:29 PM Hector Martin <marcan@marcan.st> wrote:

> And so we're back to embedded platforms like Android phones and other
> SoC stuff... user-controlled secureboot is already somewhat rare here,
> and even rarer are the cases where the user controls the whole chain
> including the TEE if any (otherwise it'll be using RPMB already); this
> pretty much excludes all production Android phones except for a few
> designed as completely open systems; we're left with those and a subset
> of dev boards (e.g. the Jetson TX1 I did fuse experiments on). In the
> end, those systems will probably end up with fairly bespoke set-ups for
> any given device or SoC family, for using RPMB.

Hehe. I think we have different ideas of "user-controlled" here,
our "users" include OP-TEE, which develop and deploy a TEE
which is open source.
https://www.op-tee.org/
Joakim who works on this project is on CC he's just not saying
anything (yet).

This project is forked and deployed by different Android and
other Arm SoC-using vendors.

Some vendors have written their own TEE from scratch.

So our users include these guys. :) As in: they take an active
interest in what we are designing here. They have access to
devices where they can replace the whole secure world for
development. They work actively with the kernel and created
the drivers/tee subsystem which is the pipe where the kernel
and the TEE communicate.

> But then again, if you have a full secureboot system where you control
> the TEE level, wouldn't you want to put the RPMB shenanigans there and
> get some semblance of secure TPM/keystore/attempt throttling
> functionality that is robust against Linux exploits and has a smaller
> attack surface? Systems without EL3 are rare (Apple M1 :-)) so it makes
> more sense to do this on those that do have it. If you're paranoid
> enough to be getting into building your own secure system with
> anti-rollback for retry counters, you should be heading in that directly
> anyway.
>
> And now Linux's RPMB code is useless because you're running the stack in
> the secure monitor instead :-)

The way OP-TEE makes use of RPMB is to call out to a userspace
daemon called tee-supplicant, which issues ioctl()s down to the
eMMC device to read/write counters. AFAIK other TEE implementations
use a similar scheme. (Judging from feedback I got when rewriting
the RPMB code in the MMC subsystem, it mattered to them.)
Their source code is here:
https://github.com/OP-TEE/optee_client/blob/master/tee-supplicant/src/rpmb.c

So Linux' eMMC RPMB code is already in wide use for this, it is
what I think all Android phones are actually using to read/write RPMB
counters. It's not like they're accessing RPMB "on the side" or
so. (I might be wrong!)

Since reading/writing RPMB on eMMC needs to be serialized
alongside Linux' read/write commands it is absolutely necessary
that the secure world and the Linux storage drivers cooperate
so the solution is to let Linux handle this arbitration.

Now the question for this patch set is: is TEE software the only
user we need to care about?

Yours,
Linus Walleij

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-11 20:57                           ` Alex Bennée
@ 2021-03-12 10:00                             ` Linus Walleij
  0 siblings, 0 replies; 47+ messages in thread
From: Linus Walleij @ 2021-03-12 10:00 UTC (permalink / raw)
  To: Alex Bennée
  Cc: Hector Martin, Sumit Garg, Arnd Bergmann,
	open list:ASYMMETRIC KEYS, David Howells, Jarkko Sakkinen,
	Joakim Bech, linux-kernel, Maxim Uvarov, Ilias Apalodimas,
	Ruchika Gupta, Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell,
	hmo, linux-mmc, linux-scsi, Ulf Hansson, Arnd Bergmann

On Thu, Mar 11, 2021 at 10:08 PM Alex Bennée <alex.bennee@linaro.org> wrote:

> I guess what we are saying is that real secure monitors should come up
> with their own common API for interfacing with RPMB devices without
> looking to the Linux kernel for inspiration?

The problem is that eMMC at least (I don't know about NVME etc)
has serialized commands, meaning you execute one command at
a time (some augmentation exist nowadays to speed up things).

When it comes to RPMB, the eMMC device must stop all other
activity (such as reading/writing any blocks to the eMMC) send a
special command to switch to the RPMB  partition, then execute
the RPMB command(s) then send a special command to switch
back to ordinary storage.

Someone has to be in control of the eMMC arbitration. Right now
Linux MMC/SD storage stack does this.

If the secure world want to use RPMB independently of the Linux
kernel there are two solutions:

- Mount a second eMMC just for the secure world - looks expensive
  so some other secure counter storage would likely be cheaper
  and it inevitably increases the BOM which is sensitive to
  manufacturers (this option is unrealistic)

- Let the secure world arbitrate all access to the eMMC - looks
  inefficient and also dangerous since the secure world now has to
  implement everything in drivers/mmc/core which is a few 100
  KB of really complex code that need to be maintained perpetually.
  (IMO this option is also unrealistic for performance and
  maintenance reasons, but who knows what secure world
  imperialists are out there).

This leaves Linux in control of the eMMC RPMB under all
circumstances as far as I can see.

Yours,
Linus Walleij

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-11 20:29                         ` Hector Martin
  2021-03-11 20:57                           ` Alex Bennée
  2021-03-12  9:47                           ` Linus Walleij
@ 2021-03-12 11:59                           ` Sumit Garg
  2021-03-12 12:08                             ` Ilias Apalodimas
  2 siblings, 1 reply; 47+ messages in thread
From: Sumit Garg @ 2021-03-12 11:59 UTC (permalink / raw)
  To: Hector Martin
  Cc: Linus Walleij, Arnd Bergmann, open list:ASYMMETRIC KEYS,
	David Howells, Jarkko Sakkinen, Joakim Bech, Alex Bennée,
	linux-kernel, Maxim Uvarov, Ilias Apalodimas, Ruchika Gupta,
	Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell, hmo,
	linux-mmc, linux-scsi, linux-nvme, Ulf Hansson, Arnd Bergmann

On Fri, 12 Mar 2021 at 01:59, Hector Martin <marcan@marcan.st> wrote:
>
> On 11/03/2021 23.31, Linus Walleij wrote:
> > I understand your argument, is your position such that the nature
> > of the hardware is such that community should leave this hardware
> > alone and not try to make use of RPMB  for say ordinary (self-installed)
> > Linux distributions?
>
> It's not really that the community should leave this hardware alone, so
> much that I think there is a very small subset of users who will be able
> to benefit from it, and that subset will be happy with a usable
> kernel/userspace interface and some userspace tooling for this purpose,
> including provisioning and such.
>
> Consider the prerequisites for using RPMB usefully here:
>
> * You need (user-controlled) secureboot

Agree with this prerequisite since secure boot is essential to build
initial trust in any system whether that system employs TEE, TPM,
secure elements etc.

> * You need secret key storage - so either some kind of CPU-fused key, or
> one protected by a TPM paired with the secureboot (key sealed to PCR
> values and such)
> * But if you have a TPM, that can handle secure counters for you already
> AIUI, so you don't need RPMB

Does TPM provide replay protected memory to store information such as:
- PIN retry timestamps?
- Hash of encrypted nvme? IMO, having replay protection for user data
on encrypted nvme is a valid use-case.

> * So this means you must be running a non-TPM secureboot system
>

AFAIK, there exist such systems which provide you with a hardware
crypto accelerator (like CAAM on i.Mx SoCs) that can protect your keys
(in this case RPMB key) and hence can leverage RPMB for replay
protection.

> And so we're back to embedded platforms like Android phones and other
> SoC stuff... user-controlled secureboot is already somewhat rare here,
> and even rarer are the cases where the user controls the whole chain
> including the TEE if any (otherwise it'll be using RPMB already); this
> pretty much excludes all production Android phones except for a few
> designed as completely open systems; we're left with those and a subset
> of dev boards (e.g. the Jetson TX1 I did fuse experiments on). In the
> end, those systems will probably end up with fairly bespoke set-ups for
> any given device or SoC family, for using RPMB.
>
> But then again, if you have a full secureboot system where you control
> the TEE level, wouldn't you want to put the RPMB shenanigans there and
> get some semblance of secure TPM/keystore/attempt throttling
> functionality that is robust against Linux exploits and has a smaller
> attack surface? Systems without EL3 are rare (Apple M1 :-)) so it makes
> more sense to do this on those that do have it. If you're paranoid
> enough to be getting into building your own secure system with
> anti-rollback for retry counters, you should be heading in that directly
> anyway.
>
> And now Linux's RPMB code is useless because you're running the stack in
> the secure monitor instead :-)
>

As Linus mentioned in other reply, there are limitations in order to
put eMMC/RPMB drivers in TEE / secure monitor such as:
- One of the design principle for a TEE is to keep its footprint as
minimal as possible like in OP-TEE we generally try to rely on Linux
for filesystem services, RPMB access etc. And currently we rely on a
user-space daemon (tee-supplicant) for RPMB access which IMO isn't as
secure as compared to direct RPMB access via kernel.
- Most embedded systems possess a single storage device (like eMMC)
for which the kernel needs to play an arbiter role.

It looks like other TEE implementations such as Trusty on Android [1]
and QSEE [2] have a similar interface for RPMB access.

So it's definitely useful for various TEE implementations to have
direct RPMB access via kernel. And while we are at it, I think it
should be useful (given replay protection benefits) to provide an
additional kernel interface to RPMB leveraging Trusted and Encrypted
Keys subsystem.

[1] https://android.googlesource.com/trusty/app/storage/
[2] https://www.qualcomm.com/media/documents/files/guard-your-data-with-the-qualcomm-snapdragon-mobile-platform.pdf

-Sumit

> --
> Hector Martin (marcan@marcan.st)
> Public Key: https://mrcn.st/pub

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

* Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem
  2021-03-12 11:59                           ` Sumit Garg
@ 2021-03-12 12:08                             ` Ilias Apalodimas
  0 siblings, 0 replies; 47+ messages in thread
From: Ilias Apalodimas @ 2021-03-12 12:08 UTC (permalink / raw)
  To: Sumit Garg
  Cc: Hector Martin, Linus Walleij, Arnd Bergmann,
	open list:ASYMMETRIC KEYS, David Howells, Jarkko Sakkinen,
	Joakim Bech, Alex Bennée, linux-kernel, Maxim Uvarov,
	Ruchika Gupta, Winkler, Tomas, yang.huang, bing.zhu, Matti.Moell,
	hmo, linux-mmc, linux-scsi, linux-nvme, Ulf Hansson,
	Arnd Bergmann

On Fri, Mar 12, 2021 at 05:29:20PM +0530, Sumit Garg wrote:
> On Fri, 12 Mar 2021 at 01:59, Hector Martin <marcan@marcan.st> wrote:
> >
> > On 11/03/2021 23.31, Linus Walleij wrote:
> > > I understand your argument, is your position such that the nature
> > > of the hardware is such that community should leave this hardware
> > > alone and not try to make use of RPMB  for say ordinary (self-installed)
> > > Linux distributions?
> >
> > It's not really that the community should leave this hardware alone, so
> > much that I think there is a very small subset of users who will be able
> > to benefit from it, and that subset will be happy with a usable
> > kernel/userspace interface and some userspace tooling for this purpose,
> > including provisioning and such.
> >
> > Consider the prerequisites for using RPMB usefully here:
> >
> > * You need (user-controlled) secureboot
> 
> Agree with this prerequisite since secure boot is essential to build
> initial trust in any system whether that system employs TEE, TPM,
> secure elements etc.
> 
> > * You need secret key storage - so either some kind of CPU-fused key, or
> > one protected by a TPM paired with the secureboot (key sealed to PCR
> > values and such)
> > * But if you have a TPM, that can handle secure counters for you already
> > AIUI, so you don't need RPMB
> 
> Does TPM provide replay protected memory to store information such as:
> - PIN retry timestamps?
> - Hash of encrypted nvme? IMO, having replay protection for user data
> on encrypted nvme is a valid use-case.
> 
> > * So this means you must be running a non-TPM secureboot system
> >
> 
> AFAIK, there exist such systems which provide you with a hardware
> crypto accelerator (like CAAM on i.Mx SoCs) that can protect your keys
> (in this case RPMB key) and hence can leverage RPMB for replay
> protection.
> 
> > And so we're back to embedded platforms like Android phones and other
> > SoC stuff... user-controlled secureboot is already somewhat rare here,
> > and even rarer are the cases where the user controls the whole chain
> > including the TEE if any (otherwise it'll be using RPMB already); this
> > pretty much excludes all production Android phones except for a few
> > designed as completely open systems; we're left with those and a subset
> > of dev boards (e.g. the Jetson TX1 I did fuse experiments on). In the
> > end, those systems will probably end up with fairly bespoke set-ups for
> > any given device or SoC family, for using RPMB.
> >
> > But then again, if you have a full secureboot system where you control
> > the TEE level, wouldn't you want to put the RPMB shenanigans there and
> > get some semblance of secure TPM/keystore/attempt throttling
> > functionality that is robust against Linux exploits and has a smaller
> > attack surface? Systems without EL3 are rare (Apple M1 :-)) so it makes
> > more sense to do this on those that do have it. If you're paranoid
> > enough to be getting into building your own secure system with
> > anti-rollback for retry counters, you should be heading in that directly
> > anyway.
> >
> > And now Linux's RPMB code is useless because you're running the stack in
> > the secure monitor instead :-)
> >
> 
> As Linus mentioned in other reply, there are limitations in order to
> put eMMC/RPMB drivers in TEE / secure monitor such as:
> - One of the design principle for a TEE is to keep its footprint as
> minimal as possible like in OP-TEE we generally try to rely on Linux
> for filesystem services, RPMB access etc. And currently we rely on a
> user-space daemon (tee-supplicant) for RPMB access which IMO isn't as
> secure as compared to direct RPMB access via kernel.
> - Most embedded systems possess a single storage device (like eMMC)
> for which the kernel needs to play an arbiter role.

Yep exactly. This is a no-go for small embedded platforms with a single eMMC.
The device *must* be in the non-secure world, so the bootloader can load the
kernel/rootfs from the eMMC.  If you restrain that in secure world access
only, that complicates things a lot ...

> 
> It looks like other TEE implementations such as Trusty on Android [1]
> and QSEE [2] have a similar interface for RPMB access.
> 
> So it's definitely useful for various TEE implementations to have
> direct RPMB access via kernel. And while we are at it, I think it
> should be useful (given replay protection benefits) to provide an
> additional kernel interface to RPMB leveraging Trusted and Encrypted
> Keys subsystem.

As for the EFI that was mentioned earlier, newer Arm devices and the
standardization behind those, is actually trying to use UEFI on most
of the platforms.  FWIW we already have code that manages the EFI variables in
the RPMB (which is kind of irrelevant to the current discussion, just giving
people a heads up).

Regards
/Ilias
> 
> [1] https://android.googlesource.com/trusty/app/storage/
> [2] https://www.qualcomm.com/media/documents/files/guard-your-data-with-the-qualcomm-snapdragon-mobile-platform.pdf
> 
> -Sumit
> 
> > --
> > Hector Martin (marcan@marcan.st)
> > Public Key: https://mrcn.st/pub

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

end of thread, other threads:[~2021-03-12 12:08 UTC | newest]

Thread overview: 47+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-03 13:54 [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend Alex Bennée
2021-03-03 13:54 ` [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem Alex Bennée
2021-03-03 15:28   ` Ulf Hansson
2021-03-03 19:37     ` Alex Bennée
2021-03-04 20:56   ` Arnd Bergmann
2021-03-05  7:51     ` Joakim Bech
2021-03-05  8:44       ` Arnd Bergmann
2021-03-08 16:20         ` Linus Walleij
2021-03-09 21:09           ` Hector Martin
2021-03-10  5:14             ` Sumit Garg
2021-03-10  8:47               ` Hector Martin
2021-03-10  9:48                 ` Linus Walleij
2021-03-10 13:52                   ` Hector Martin
2021-03-11  0:36                     ` Linus Walleij
2021-03-11  9:22                       ` Hector Martin
2021-03-11 14:06                         ` Linus Walleij
2021-03-11 20:02                           ` Hector Martin
2021-03-12  9:22                             ` Linus Walleij
2021-03-10 10:29                 ` Sumit Garg
2021-03-11  0:49                   ` Linus Walleij
2021-03-11  1:07                     ` James Bottomley
2021-03-11  9:45                     ` Hector Martin
2021-03-11 14:31                       ` Linus Walleij
2021-03-11 20:29                         ` Hector Martin
2021-03-11 20:57                           ` Alex Bennée
2021-03-12 10:00                             ` Linus Walleij
2021-03-12  9:47                           ` Linus Walleij
2021-03-12 11:59                           ` Sumit Garg
2021-03-12 12:08                             ` Ilias Apalodimas
2021-03-09 17:12         ` David Howells
2021-03-10  4:54           ` Sumit Garg
2021-03-10  9:33           ` Linus Walleij
2021-03-03 13:54 ` [RFC PATCH 2/5] char: rpmb: provide a user space interface Alex Bennée
2021-03-04  7:01   ` Winkler, Tomas
2021-03-04 10:19     ` Alex Bennée
2021-03-04 10:34       ` Winkler, Tomas
2021-03-04 17:52         ` Alex Bennée
2021-03-04 19:54           ` Winkler, Tomas
2021-03-04 21:43             ` Arnd Bergmann
2021-03-05  6:31               ` Winkler, Tomas
2021-03-04 21:08   ` Arnd Bergmann
2021-03-03 13:54 ` [RFC PATCH 3/5] tools rpmb: add RPBM access tool Alex Bennée
2021-03-03 13:54 ` [RFC PATCH 4/5] rpmb: create virtio rpmb frontend driver [WIP] Alex Bennée
2021-03-03 13:55 ` [RFC PATCH 5/5] tools/rpmb: simple test sequence Alex Bennée
2021-03-09 13:27 ` [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend Avri Altman
2021-03-10 14:29   ` Alex Bennée
2021-03-11 13:45     ` Avri Altman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).