mbox series

[RFC,0/5] RPMB internal and user-space API + WIP virtio-rpmb frontend

Message ID 20210303135500.24673-1-alex.bennee@linaro.org
Headers show
Series RPMB internal and user-space API + WIP virtio-rpmb frontend | expand

Message

Alex Bennée March 3, 2021, 1:54 p.m. UTC
Hi,

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

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

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

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

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

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

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

  - frame construction left to device driver

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

  - key management uses keyctl()

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

  - user-space interface split across several ioctls

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

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

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


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

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

Comments

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

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

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

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

Kind regards
Uffe

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

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

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

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

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

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

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

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

> > The detailed operation of implementing the access is left to the TEE
> > device driver itself.
> >
> > [This is based-on Thomas Winkler's proposed API from:
> >
> >   https://lore.kernel.org/linux-mmc/1478548394-8184-2-git-send-email-tomas.winkler@intel.com/
> >
> > The principle difference is the framing details and HW specific
> > bits (JDEC vs NVME frames) are left to the lower level TEE driver to
> > worry about. The eventual userspace ioctl interface will aim to be
> > similarly generic. This is an RFC to follow up on:
> >
> >   Subject: RPMB user space ABI
> >   Date: Thu, 11 Feb 2021 14:07:00 +0000
> >   Message-ID: <87mtwashi4.fsf@linaro.org>]
> >
> > Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> > Cc: Tomas Winkler <tomas.winkler@intel.com>
> > Cc: Ulf Hansson <ulf.hansson@linaro.org>
> > Cc: Linus  Walleij <linus.walleij@linaro.org>
> > Cc: Arnd Bergmann <arnd.bergmann@linaro.org>
> > Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> > ---
> >  MAINTAINERS                |   7 +
> >  drivers/char/Kconfig       |   2 +
> >  drivers/char/Makefile      |   1 +
> >  drivers/char/rpmb/Kconfig  |  11 +
> >  drivers/char/rpmb/Makefile |   7 +
> >  drivers/char/rpmb/core.c   | 429 +++++++++++++++++++++++++++++++++++++
> >  include/linux/rpmb.h       | 163 ++++++++++++++
> 
> 
> My feeling is that it should be a top-level subsystem, in drivers/rpmb
> rather than drivers/char/rpmb, as you implement an abstraction layer
> that other drivers can plug into, rather than a simple driver.
> 
>        Arnd
Arnd Bergmann March 5, 2021, 8:44 a.m. UTC | #4
On Fri, Mar 5, 2021 at 8:52 AM Joakim Bech <joakim.bech@linaro.org> wrote:
> On Thu, Mar 04, 2021 at 09:56:24PM +0100, Arnd Bergmann wrote:
> > On Wed, Mar 3, 2021 at 2:54 PM Alex Bennée <alex.bennee@linaro.org> wrote:
> > That said, I can also imagine use cases where we do want to
> > store the key in the kernel's keyring, so maybe we end up needing
> > both.
> >
> The concern I have in those cases is that you need to share the RPMB key
> in some way if you need to access the RPMB device from secure side as
> well as from the non-secure side. Technically doable I guess, but in
> practice and in terms of security it doesn't seem like a good approach.
>
> In a shared environment like that you also have the problem that you
> need to agree on how to actually store files on the RPMB device. OP-TEE
> has it's own "FAT-look-a-like" implementation when using RPMB. But if
> you need mutual access, then you need to get into agreement on where to
> actually store the files in the RPMB.
>
> However, if secure side for some reason doesn't use RPMB at all, then
> kernel could of course take control of it and use it.
>
> I would probably not spend too much time on taking that use case into
> account until we actually see a real need for it.

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

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

      Arnd
Linus Walleij March 8, 2021, 4:20 p.m. UTC | #5
On Fri, Mar 5, 2021 at 9:44 AM Arnd Bergmann <arnd@linaro.org> wrote:

> I think the scenario for the 'nvme-rpmb' tool that does the signing in user

> space does not involve any TEE at the moment, because PCs usually

> don't have one.


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

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

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

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

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

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

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

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

> I agree that sharing the RPMB is not a great idea, so if you have a TEE

> in the system that requires an RPMB for storage, it won't be usable by

> anything else. However, you can have multiple RPMB partitions with separate

> keys on an NVMe drive, and you can easily have multiple emulated

> virtio-rpmb devices  in a guest and use them for purposes other than the

> TEE.


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

Yours,
Linus Walleij
Avri Altman March 9, 2021, 1:27 p.m. UTC | #6
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
David Howells March 9, 2021, 5:12 p.m. UTC | #7
Linus Walleij <linus.walleij@linaro.org> wrote:

> As it seems neither Microsoft nor Apple is paying it much attention

> (+/- new facts) it will be up to the community to define use cases

> for RPMB. I don't know what would make most sense, but the

> kernel keyring seems to make a bit of sense as it is a well maintained

> keyring project.


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

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

David
Hector Martin March 9, 2021, 9:09 p.m. UTC | #8
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.
Sumit Garg March 10, 2021, 4:54 a.m. UTC | #9
Hi David,

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

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

-Sumit

> David
>
Hector Martin March 10, 2021, 8:47 a.m. UTC | #10
On 10/03/2021 14.14, Sumit Garg wrote:
> On Wed, 10 Mar 2021 at 02:47, Hector Martin <marcan@marcan.st> wrote:

>>

>> On 09/03/2021 01.20, Linus Walleij wrote:

>>> I suppose it would be a bit brutal if the kernel would just go in and

>>> appropriate any empty RPMB it finds, but I suspect it is the right way

>>> to make use of this facility given that so many of them are just sitting

>>> there unused. Noone will run $CUSTOM_UTILITY any more than they

>>> run the current RPMB tools in mmc-tools.

>>

>> AIUI the entire thing relies on a shared key that is programmed once

>> into the RPMB device, which is a permanent operation. This key has to be

>> secure, usually stored on CPU fuses or derived based on such a root of

>> trust. To me it would seem ill-advised to attempt to automate this

>> process and have the kernel do a permanent take-over of any RPMBs it

>> finds (with what key, for one?) :)

>>

> 

> Wouldn't it be a good idea to use DT here to represent whether a

> particular RPMB is used as a TEE backup or is available for normal

> kernel usage?

> 

> In case of normal kernel usage, I think the RPMB key can come from

> trusted and encrypted keys subsystem.


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

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

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

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

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub
Sumit Garg March 10, 2021, 10:29 a.m. UTC | #11
On Wed, 10 Mar 2021 at 14:17, Hector Martin <marcan@marcan.st> wrote:
>

> On 10/03/2021 14.14, Sumit Garg wrote:

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

> >>

> >> On 09/03/2021 01.20, Linus Walleij wrote:

> >>> I suppose it would be a bit brutal if the kernel would just go in and

> >>> appropriate any empty RPMB it finds, but I suspect it is the right way

> >>> to make use of this facility given that so many of them are just sitting

> >>> there unused. Noone will run $CUSTOM_UTILITY any more than they

> >>> run the current RPMB tools in mmc-tools.

> >>

> >> AIUI the entire thing relies on a shared key that is programmed once

> >> into the RPMB device, which is a permanent operation. This key has to be

> >> secure, usually stored on CPU fuses or derived based on such a root of

> >> trust. To me it would seem ill-advised to attempt to automate this

> >> process and have the kernel do a permanent take-over of any RPMBs it

> >> finds (with what key, for one?) :)

> >>

> >

> > Wouldn't it be a good idea to use DT here to represent whether a

> > particular RPMB is used as a TEE backup or is available for normal

> > kernel usage?

> >

> > In case of normal kernel usage, I think the RPMB key can come from

> > trusted and encrypted keys subsystem.

>

> Remember that if the key is ever lost, the RPMB is now completely

> useless forever.

>

> This is why, as far as I know, most sane platforms will use hard fused

> values to derive this kind of thing, not any kind of key stored in

> erasable storage.


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

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

>

> Also, newly provisioned keys are sent in plain text, which means that

> any kind of "if the RPMB is blank, take it over" automation equates to

> handing over your key who an attacker who removes the RPMB and replaces

> it with a blank one, and then they can go access anything they want on

> the old RPMB device (assuming the key hasn't changed; and if it has

> changed that's conversely a recipe for data loss if something goes wrong).

>

> I really think trying to automate any kind of "default" usage of an RPMB

> is a terrible idea. It needs to be a conscious decision on a

> per-platform basis.

>


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

-Sumit

> --

> Hector Martin (marcan@marcan.st)

> Public Key: https://mrcn.st/pub
Hector Martin March 10, 2021, 1:52 p.m. UTC | #12
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.
Alex Bennée March 10, 2021, 2:29 p.m. UTC | #13
Avri Altman <avri.altman@wdc.com> writes:

> The mmc driver has some hooks to support rpmb access, but access is

> mainly facilitated from user space, e.g. mmc-utils.

>

> The ufs driver has no concept of rpmb access - it is facilitated via

> user space, e.g. ufs-utils and similar.

>

> Both for ufs and mmc, rpmb access is defined in their applicable jedec

> specs. This is the case for few years now - AFAIK No new rpmb-related

> stuff is expected to be introduced in the near future.

>

> What problems, as far as mmc and ufs, are you trying to solve by this

> new subsystem?


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

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

>

> Thanks,

> Avri



-- 
Alex Bennée
Linus Walleij March 11, 2021, 12:49 a.m. UTC | #14
On Wed, Mar 10, 2021 at 11:29 AM Sumit Garg <sumit.garg@linaro.org> wrote:

> And RPMB key provisioning

> being a one time process should be carried out carefully during device

> manufacturing only.


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

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

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

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

Yours,
Linus Walleij
James Bottomley March 11, 2021, 1:07 a.m. UTC | #15
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
Hector Martin March 11, 2021, 9:45 a.m. UTC | #16
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.
Avri Altman March 11, 2021, 1:45 p.m. UTC | #17
> Avri Altman <avri.altman@wdc.com> writes:

> 

> > The mmc driver has some hooks to support rpmb access, but access is

> > mainly facilitated from user space, e.g. mmc-utils.

> >

> > The ufs driver has no concept of rpmb access - it is facilitated via

> > user space, e.g. ufs-utils and similar.

> >

> > Both for ufs and mmc, rpmb access is defined in their applicable jedec

> > specs. This is the case for few years now - AFAIK No new rpmb-related

> > stuff is expected to be introduced in the near future.

> >

> > What problems, as far as mmc and ufs, are you trying to solve by this

> > new subsystem?

> 

> Well in my case the addition of virtio-rpmb. As yet another RPMB device

> which only supports RPMB transactions and isn't part of a wider block

> device. The API dissonance comes into play when looking to implement it

> as part of wider block device stacks and then having to do things like

> fake 0 length eMMC devices with just an RPMB partition to use existing

> tools.

> 

> I guess that was the original attraction of having a common kernel

> subsystem to interact with RPMB functionality regardless of the

> underlying HW. However from the other comments it seems the preference

> is just to leave it to user-space and domain specific tools for each

> device type.

Yes.  It took us a while to clean those tools to perform by-spec rpmb access.

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

Thanks,
Avri
Linus Walleij March 11, 2021, 2:06 p.m. UTC | #18
On Thu, Mar 11, 2021 at 10:22 AM Hector Martin <marcan@marcan.st> wrote:
> On 11/03/2021 09.36, Linus Walleij wrote:


> > The typical use-case mentioned in one reference is to restrict

> > the number of password/pin attempts and  combine that with

> > secure time to make sure that longer and longer intervals are

> > required between password attempts.

> >

> > This seems pretty neat to me.

>

> Yes, but to implement that you don't need any secure storage *at all*.

> If all the RPMB did was authenticate an incrementing counter, you could

> just store the <last timestamp, attempts remaining> tuple inside a blob

> of secure (encrypted and MACed) storage on any random Flash device,

> along with the counter value, and thus prevent rollbacks that way (some

> finer design points are needed to deal with power loss protection and

> ordering, but the theory holds).


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

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

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

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

> > With RPMB this can be properly protected against because

> > the next attempt can not be made until after the RPMB

> > monotonic counter has been increased.

>

> But this is only enforced by software. If you do not have secure boot,

> you can just patch software to allow infinite tries without touching the

> RPMB. The RPMB doesn't check PINs for you, it doesn't even gate read

> access to data in any way. All it does is promise you cannot make the

> counter count down, or make the data stored within go back in time.


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

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

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

Yours,
Linus Walleij
Linus Walleij March 11, 2021, 2:31 p.m. UTC | #19
Hi Marcan,

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

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

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

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

I kind of agree.

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

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

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

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

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

Yes.

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

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

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

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

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

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

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

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

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

Yours,
Linus Walleij
Hector Martin March 11, 2021, 8:29 p.m. UTC | #20
On 11/03/2021 23.31, Linus Walleij wrote:
> I understand your argument, is your position such that the nature

> of the hardware is such that community should leave this hardware

> alone and not try to make use of RPMB  for say ordinary (self-installed)

> Linux distributions?


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

Consider the prerequisites for using RPMB usefully here:

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

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

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

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

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub
Alex Bennée March 11, 2021, 8:57 p.m. UTC | #21
Hector Martin <marcan@marcan.st> writes:

> On 11/03/2021 23.31, Linus Walleij wrote:

>> I understand your argument, is your position such that the nature

>> of the hardware is such that community should leave this hardware

>> alone and not try to make use of RPMB  for say ordinary (self-installed)

>> Linux distributions?

>

> It's not really that the community should leave this hardware alone, so 

> much that I think there is a very small subset of users who will be able 

> to benefit from it, and that subset will be happy with a usable 

> kernel/userspace interface and some userspace tooling for this purpose, 

> including provisioning and such.

>

> Consider the prerequisites for using RPMB usefully here:

>

> * You need (user-controlled) secureboot

> * You need secret key storage - so either some kind of CPU-fused key, or 

> one protected by a TPM paired with the secureboot (key sealed to PCR 

> values and such)

> * But if you have a TPM, that can handle secure counters for you already 

> AIUI, so you don't need RPMB

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

>

> And so we're back to embedded platforms like Android phones and other 

> SoC stuff... user-controlled secureboot is already somewhat rare here, 

> and even rarer are the cases where the user controls the whole chain 

> including the TEE if any (otherwise it'll be using RPMB already); this 

> pretty much excludes all production Android phones except for a few 

> designed as completely open systems; we're left with those and a subset 

> of dev boards (e.g. the Jetson TX1 I did fuse experiments on). In the 

> end, those systems will probably end up with fairly bespoke set-ups for 

> any given device or SoC family, for using RPMB.

>

> But then again, if you have a full secureboot system where you control 

> the TEE level, wouldn't you want to put the RPMB shenanigans there and 

> get some semblance of secure TPM/keystore/attempt throttling 

> functionality that is robust against Linux exploits and has a smaller 

> attack surface? Systems without EL3 are rare (Apple M1 :-)) so it makes 

> more sense to do this on those that do have it. If you're paranoid 

> enough to be getting into building your own secure system with 

> anti-rollback for retry counters, you should be heading in that directly 

> anyway.

>

> And now Linux's RPMB code is useless because you're running the stack in 

> the secure monitor instead :-)


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

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

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

-- 
Alex Bennée
Linus Walleij March 12, 2021, 9:22 a.m. UTC | #22
Hi Hector,

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

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

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

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

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

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

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

Clever, isn't it? :)

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

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

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

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

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

Yep you're right.

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

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

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

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

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

I agree.

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

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

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

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

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

Yours,
Linus Walleij
Linus Walleij March 12, 2021, 9:47 a.m. UTC | #23
Hi Hector,

I see a misunderstanding here :) explaining below.

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

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

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

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

Some vendors have written their own TEE from scratch.

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

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

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

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

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

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

Yours,
Linus Walleij
Linus Walleij March 12, 2021, 10 a.m. UTC | #24
On Thu, Mar 11, 2021 at 10:08 PM Alex Bennée <alex.bennee@linaro.org> wrote:

> I guess what we are saying is that real secure monitors should come up

> with their own common API for interfacing with RPMB devices without

> looking to the Linux kernel for inspiration?


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

-Sumit

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

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

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

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

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