2021-03-04 10:58:12

by Alex Bennée

[permalink] [raw]
Subject: [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend

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: <[email protected]>

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


2021-03-04 11:00:36

by Alex Bennée

[permalink] [raw]
Subject: [RFC PATCH 4/5] rpmb: create virtio rpmb frontend driver [WIP]

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 <[email protected]>
Cc: Tomas Winkler <[email protected]>

---
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

2021-03-04 23:21:26

by Alex Bennée

[permalink] [raw]
Subject: [RFC PATCH 2/5] char: rpmb: provide a user space interface

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 <[email protected]>
Cc: Ulf Hansson <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Ilias Apalodimas <[email protected]>
Cc: Tomas Winkler <[email protected]>
Cc: Alexander Usyskin <[email protected]>
Cc: Avri Altman <[email protected]>
---
.../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:[email protected]>
0xB6 all linux/fpga-dfl.h
0xB7 all uapi/linux/remoteproc_cdev.h <mailto:[email protected]>
+0xB8 80-8F uapi/linux/rpmb.h <mailto:[email protected]>
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: [email protected]
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

2021-03-04 23:22:21

by Alex Bennée

[permalink] [raw]
Subject: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

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/[email protected]/

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: <[email protected]>]

Signed-off-by: Alex Bennée <[email protected]>
Cc: Tomas Winkler <[email protected]>
Cc: Ulf Hansson <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Ilias Apalodimas <[email protected]>
---
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: [email protected]
+S: Supported
+F: drivers/char/rpmb/*
+F: include/linux/rpmb.h
+
RTL2830 MEDIA DRIVER
M: Antti Palosaari <[email protected]>
L: [email protected]
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

2021-03-04 23:23:04

by Alex Bennée

[permalink] [raw]
Subject: [RFC PATCH 3/5] tools rpmb: add RPBM access tool

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 <[email protected]>
Cc: Tomas Winkler <[email protected]>
Cc: Alexander Usyskin <[email protected]>
---
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 <[email protected]>
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

2021-03-05 00:05:04

by Winkler, Tomas

[permalink] [raw]
Subject: RE: [RFC PATCH 2/5] char: rpmb: provide a user space interface


>
> 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 <[email protected]>
> Cc: Ulf Hansson <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: Arnd Bergmann <[email protected]>
> Cc: Ilias Apalodimas <[email protected]>
> Cc: Tomas Winkler <[email protected]>
> Cc: Alexander Usyskin <[email protected]>
> Cc: Avri Altman <[email protected]>
> ---
> .../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-
> [email protected]>
> 0xB6 all linux/fpga-dfl.h
> 0xB7 all uapi/linux/remoteproc_cdev.h <mailto:linux-
> [email protected]>
> +0xB8 80-8F uapi/linux/rpmb.h <mailto:linux-
> [email protected]>
> 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: [email protected]
> 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

2021-03-05 00:57:57

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH 2/5] char: rpmb: provide a user space interface

On Wed, Mar 3, 2021 at 2:54 PM Alex Bennée <[email protected]> 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

2021-03-05 00:58:24

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Wed, Mar 3, 2021 at 2:54 PM Alex Bennée <[email protected]> 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/[email protected]/
>
> 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: <[email protected]>]
>
> Signed-off-by: Alex Bennée <[email protected]>
> Cc: Tomas Winkler <[email protected]>
> Cc: Ulf Hansson <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: Arnd Bergmann <[email protected]>
> Cc: Ilias Apalodimas <[email protected]>
> ---
> 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

2021-03-05 07:54:27

by Joakim Bech

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

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 <[email protected]> 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/[email protected]/
> >
> > 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: <[email protected]>]
> >
> > Signed-off-by: Alex Bennée <[email protected]>
> > Cc: Tomas Winkler <[email protected]>
> > Cc: Ulf Hansson <[email protected]>
> > Cc: Linus Walleij <[email protected]>
> > Cc: Arnd Bergmann <[email protected]>
> > Cc: Ilias Apalodimas <[email protected]>
> > ---
> > 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

2021-03-05 08:46:18

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Fri, Mar 5, 2021 at 8:52 AM Joakim Bech <[email protected]> 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 <[email protected]> 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

2021-03-08 16:24:31

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Fri, Mar 5, 2021 at 9:44 AM Arnd Bergmann <[email protected]> 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

2021-03-09 13:31:20

by Avri Altman

[permalink] [raw]
Subject: [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend

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

2021-03-09 17:14:28

by David Howells

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

Linus Walleij <[email protected]> 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

2021-03-09 21:13:21

by Hector Martin

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

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 ([email protected])
Public Key: https://mrcn.st/pub

2021-03-10 05:09:21

by Sumit Garg

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

Hi David,

On Tue, 9 Mar 2021 at 22:43, David Howells <[email protected]> wrote:
>
> Linus Walleij <[email protected]> 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
>

2021-03-10 05:17:23

by Sumit Garg

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Wed, 10 Mar 2021 at 02:47, Hector Martin <[email protected]> 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 ([email protected])
> Public Key: https://mrcn.st/pub

2021-03-10 08:51:15

by Hector Martin

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On 10/03/2021 14.14, Sumit Garg wrote:
> On Wed, 10 Mar 2021 at 02:47, Hector Martin <[email protected]> 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 ([email protected])
Public Key: https://mrcn.st/pub

2021-03-10 09:35:13

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Tue, Mar 9, 2021 at 6:12 PM David Howells <[email protected]> wrote:
> Linus Walleij <[email protected]> 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

2021-03-10 09:51:25

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Wed, Mar 10, 2021 at 9:47 AM Hector Martin <[email protected]> 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

2021-03-10 10:33:24

by Sumit Garg

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Wed, 10 Mar 2021 at 14:17, Hector Martin <[email protected]> wrote:
>
> On 10/03/2021 14.14, Sumit Garg wrote:
> > On Wed, 10 Mar 2021 at 02:47, Hector Martin <[email protected]> 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 ([email protected])
> Public Key: https://mrcn.st/pub

2021-03-10 13:54:41

by Hector Martin

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

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 ([email protected])
Public Key: https://mrcn.st/pub

2021-03-10 14:40:35

by Alex Bennée

[permalink] [raw]
Subject: Re: [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend


Avri Altman <[email protected]> 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

2021-03-11 00:44:56

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Wed, Mar 10, 2021 at 2:52 PM Hector Martin <[email protected]> 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

2021-03-11 00:53:31

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Wed, Mar 10, 2021 at 11:29 AM Sumit Garg <[email protected]> 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

2021-03-11 01:11:57

by James Bottomley

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

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


2021-03-11 09:25:15

by Hector Martin

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

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 ([email protected])
Public Key: https://mrcn.st/pub

2021-03-11 09:49:26

by Hector Martin

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

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 ([email protected])
Public Key: https://mrcn.st/pub

2021-03-11 13:47:45

by Avri Altman

[permalink] [raw]
Subject: RE: [RFC PATCH 0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend

> Avri Altman <[email protected]> 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

2021-03-11 14:09:14

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Thu, Mar 11, 2021 at 10:22 AM Hector Martin <[email protected]> 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

2021-03-11 14:34:21

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

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 <[email protected]> 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

2021-03-11 20:06:58

by Hector Martin

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

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 ([email protected])
Public Key: https://mrcn.st/pub

2021-03-11 20:31:04

by Hector Martin

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

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 ([email protected])
Public Key: https://mrcn.st/pub

2021-03-11 21:12:06

by Alex Bennée

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem


Hector Martin <[email protected]> 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

2021-03-12 09:49:25

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

Hi Hector,

I see a misunderstanding here :) explaining below.

On Thu, Mar 11, 2021 at 9:29 PM Hector Martin <[email protected]> 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

2021-03-12 10:02:25

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Thu, Mar 11, 2021 at 10:08 PM Alex Bennée <[email protected]> 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

2021-03-12 11:49:02

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

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 <[email protected]> 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

2021-03-12 12:03:14

by Sumit Garg

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Fri, 12 Mar 2021 at 01:59, Hector Martin <[email protected]> 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 ([email protected])
> Public Key: https://mrcn.st/pub

2021-03-12 12:12:18

by Ilias Apalodimas

[permalink] [raw]
Subject: Re: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

On Fri, Mar 12, 2021 at 05:29:20PM +0530, Sumit Garg wrote:
> On Fri, 12 Mar 2021 at 01:59, Hector Martin <[email protected]> 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 ([email protected])
> > Public Key: https://mrcn.st/pub