diff mbox series

[RFC,1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem

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

Commit Message

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

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

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

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

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

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

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

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

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

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

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

-- 
2.20.1

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
> >
Arnd Bergmann March 4, 2021, 8:56 p.m. UTC | #3
On Wed, Mar 3, 2021 at 2:54 PM Alex Bennée <alex.bennee@linaro.org> wrote:
>

> A number of storage technologies support a specialised hardware

> partition designed to be resistant to replay attacks. The underlying

> HW protocols differ but the operations are common. The RPMB partition

> cannot be accessed via standard block layer, but by a set of specific

> commands: WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY. Such a

> partition provides authenticated and replay protected access, hence

> suitable as a secure storage.

>

> The RPMB layer aims to provide in-kernel API for Trusted Execution

> Environment (TEE) devices that are capable to securely compute block

> frame signature. In case a TEE device wishes to store a replay

> protected data, requests the storage device via RPMB layer to store

> the data.

>

> A TEE device driver can claim the RPMB interface, for example, via

> class_interface_register(). The RPMB layer provides a series of

> operations for interacting with the device.

>

>   * program_key - a one time operation for setting up a new device

>   * get_capacity - introspect the device capacity

>   * get_write_count - check the write counter

>   * write_blocks - write a series of blocks to the RPMB device

>   * read_blocks - read a series of blocks from the RPMB device


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

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

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

> The detailed operation of implementing the access is left to the TEE

> device driver itself.

>

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

>

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

>

> The principle difference is the framing details and HW specific

> bits (JDEC vs NVME frames) are left to the lower level TEE driver to

> worry about. The eventual userspace ioctl interface will aim to be

> similarly generic. This is an RFC to follow up on:

>

>   Subject: RPMB user space ABI

>   Date: Thu, 11 Feb 2021 14:07:00 +0000

>   Message-ID: <87mtwashi4.fsf@linaro.org>]

>

> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

> Cc: Tomas Winkler <tomas.winkler@intel.com>

> Cc: Ulf Hansson <ulf.hansson@linaro.org>

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

> Cc: Arnd Bergmann <arnd.bergmann@linaro.org>

> Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>

> ---

>  MAINTAINERS                |   7 +

>  drivers/char/Kconfig       |   2 +

>  drivers/char/Makefile      |   1 +

>  drivers/char/rpmb/Kconfig  |  11 +

>  drivers/char/rpmb/Makefile |   7 +

>  drivers/char/rpmb/core.c   | 429 +++++++++++++++++++++++++++++++++++++

>  include/linux/rpmb.h       | 163 ++++++++++++++



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

       Arnd
Joakim Bech March 5, 2021, 7:51 a.m. UTC | #4
On Thu, Mar 04, 2021 at 09:56:24PM +0100, Arnd Bergmann wrote:
> On Wed, Mar 3, 2021 at 2:54 PM Alex Bennée <alex.bennee@linaro.org> wrote:

> >

> > A number of storage technologies support a specialised hardware

> > partition designed to be resistant to replay attacks. The underlying

> > HW protocols differ but the operations are common. The RPMB partition

> > cannot be accessed via standard block layer, but by a set of specific

> > commands: WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY. Such a

> > partition provides authenticated and replay protected access, hence

> > suitable as a secure storage.

> >

> > The RPMB layer aims to provide in-kernel API for Trusted Execution

> > Environment (TEE) devices that are capable to securely compute block

> > frame signature. In case a TEE device wishes to store a replay

> > protected data, requests the storage device via RPMB layer to store

> > the data.

> >

> > A TEE device driver can claim the RPMB interface, for example, via

> > class_interface_register(). The RPMB layer provides a series of

> > operations for interacting with the device.

> >

> >   * program_key - a one time operation for setting up a new device

> >   * get_capacity - introspect the device capacity

> >   * get_write_count - check the write counter

> >   * write_blocks - write a series of blocks to the RPMB device

> >   * read_blocks - read a series of blocks from the RPMB device

> 

> Based on the discussion we had today in a meeting, it seems the

> main change that is needed is to get back to the original model

> of passing the encrypted data to the kernel instead of cleartext

> data, as the main use case we know of is to have the key inside of

> the TEE device and not available to the kernel or user space.

> 

Yes, for OP-TEE we have to encrypt all data going to RPMB, since the
information goes via non-secure world. We get the integrity by applying
the HMAC with the key that is being discussed in this thread. The TEE
owns and is responsible for programming the key (and that should be
something that is achieved as part of the manufacturing process).

> This is also required to be able to forward the encrypted data

> through the same interface on a KVM host, when the guest

> uses virtio-rpmb, and the host forwards the data into an mmc or

> ufs device.

> 

> That said, I can also imagine use cases where we do want to

> store the key in the kernel's keyring, so maybe we end up needing

> both.

> 

The concern I have in those cases is that you need to share the RPMB key
in some way if you need to access the RPMB device from secure side as
well as from the non-secure side. Technically doable I guess, but in
practice and in terms of security it doesn't seem like a good approach.

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

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

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

> > The detailed operation of implementing the access is left to the TEE

> > device driver itself.

> >

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

> >

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

> >

> > The principle difference is the framing details and HW specific

> > bits (JDEC vs NVME frames) are left to the lower level TEE driver to

> > worry about. The eventual userspace ioctl interface will aim to be

> > similarly generic. This is an RFC to follow up on:

> >

> >   Subject: RPMB user space ABI

> >   Date: Thu, 11 Feb 2021 14:07:00 +0000

> >   Message-ID: <87mtwashi4.fsf@linaro.org>]

> >

> > Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

> > Cc: Tomas Winkler <tomas.winkler@intel.com>

> > Cc: Ulf Hansson <ulf.hansson@linaro.org>

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

> > Cc: Arnd Bergmann <arnd.bergmann@linaro.org>

> > Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>

> > ---

> >  MAINTAINERS                |   7 +

> >  drivers/char/Kconfig       |   2 +

> >  drivers/char/Makefile      |   1 +

> >  drivers/char/rpmb/Kconfig  |  11 +

> >  drivers/char/rpmb/Makefile |   7 +

> >  drivers/char/rpmb/core.c   | 429 +++++++++++++++++++++++++++++++++++++

> >  include/linux/rpmb.h       | 163 ++++++++++++++

> 

> 

> My feeling is that it should be a top-level subsystem, in drivers/rpmb

> rather than drivers/char/rpmb, as you implement an abstraction layer

> that other drivers can plug into, rather than a simple driver.

> 

>        Arnd


-- 
Regards,
Joakim
Arnd Bergmann March 5, 2021, 8:44 a.m. UTC | #5
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
Hector Martin March 9, 2021, 9:09 p.m. UTC | #6
On 09/03/2021 01.20, Linus Walleij wrote:
> I suppose it would be a bit brutal if the kernel would just go in and

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

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

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

> run the current RPMB tools in mmc-tools.


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

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

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

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub
Sumit Garg March 10, 2021, 4:54 a.m. UTC | #7
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

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

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

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

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

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

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

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

>

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

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

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

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

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

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

>


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

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

-Sumit

> For what it's worth, these days I think Apple uses a separate, dedicated

> secure element for replay protected storage, not RPMB. That seems like a

> sane approach, given that obviously Flash storage vendors cannot be

> trusted to write security-critical firmware. But if all you have is

> RPMB, using it is better than nothing.

>

> The main purpose of the RPMB is, as the name implies, replay protection.

> You can do secure storage on any random flash with encryption, and even

> do full authentication with hash trees, but the problem is no matter how

> fancy your scheme is, attackers can always dump all memory and roll your

> device back to the past. This defeats stuff like PIN code attempt

> limits. So it isn't so much for storing crypto keys or such, but rather

> a way to prevent these attacks.

>

> --

> Hector Martin (marcan@marcan.st)

> Public Key: https://mrcn.st/pub
Linus Walleij March 10, 2021, 9:33 a.m. UTC | #9
On Tue, Mar 9, 2021 at 6:12 PM David Howells <dhowells@redhat.com> wrote:
> Linus Walleij <linus.walleij@linaro.org> wrote:

>

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

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

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

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

> > keyring project.

>

> I'm afraid I don't know a whole lot about the RPMB.  I've just been and read

> https://lwn.net/Articles/682276/ about it.


Sorry, here is a primer on RPMB.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

> What is it you envision the keyring API doing with regard to this?

> Being used to represent the key needed to access the RPMB or

> being used to represent an RPMB entry (does it have entries?)?


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

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

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

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

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

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

Yours,
Linus Walleij
Linus Walleij March 10, 2021, 9:48 a.m. UTC | #10
On Wed, Mar 10, 2021 at 9:47 AM Hector Martin <marcan@marcan.st> wrote:

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

> useless forever.

>

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

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

> erasable storage.


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

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

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

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

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

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

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

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

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

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

>

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

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

> per-platform basis.


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

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

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

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

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

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

Yours,
Linus Walleij
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.

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub
Linus Walleij March 11, 2021, 12:36 a.m. UTC | #13
On Wed, Mar 10, 2021 at 2:52 PM Hector Martin <marcan@marcan.st> wrote:

> This relies on having a secure boot chain to start with (otherwise you

> can just bypass policy that way; the RPMB is merely storage to give you

> anti-rollback properties, it can't enforce anything itself). So you

> would have to have a laptop with a fully locked down secure boot, which

> can only boot some version of Linux signed by you until, say, LUKS

> decryption. And then the tooling around that needs to be integrated with

> RPMB, to use it as an attempt counter.


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

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

> But now this ends up having to involve userspace anyway; the kernel key

> stuff doesn't support policy like this, does it? So having the kernel

> automagically use RPMB wouldn't get us there.


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

> I may be wrong on the details here, but as far as I know RPMB is

> strictly equivalent to a simple secure increment-only counter in what it

> buys you. The stuff about writing data to it securely is all a red

> herring - you can implement secure storage elsewhere, and with secure

> storage + a single secure counter, you can implement anti-rollback.

>

> It is not intended to store keys in a way that is somehow safer than

> other mechanisms. After all, you need to securely store the RPMB key to

> begin with; you might as well use that to encrypt a keystore on any

> random block device.


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

This seems pretty neat to me.

> But RPMB does not enforce any of this policy for you. RPMB only gives

> you a primitive: the ability to have storage that cannot be externally

> rolled back. So none of this works unless the entire system is set up to

> securely boot all the way until the drive unlock happens, and there are

> no other blatant code execution avenues.


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

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

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

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

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

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

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

> There isn't even any encryption involved in the protocol, so all the

> data stored in the RPMB is public and available to any attacker.


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

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

> So unless the kernel grows a subsystem/feature to enforce complex key

> policies (with things like use counts, retry times, etc), I don't think

> there's a place to integrate RPMB kernel-side. You still need a trusted

> userspace tool to glue it all together.


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

Yours,
Linus Walleij
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:22 a.m. UTC | #16
On 11/03/2021 09.36, Linus Walleij wrote:
>> It is not intended to store keys in a way that is somehow safer than

>> other mechanisms. After all, you need to securely store the RPMB key to

>> begin with; you might as well use that to encrypt a keystore on any

>> random block device.

> 

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

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

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

> required between password attempts.

> 

> This seems pretty neat to me.


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

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

> 

>> But RPMB does not enforce any of this policy for you. RPMB only gives

>> you a primitive: the ability to have storage that cannot be externally

>> rolled back. So none of this works unless the entire system is set up to

>> securely boot all the way until the drive unlock happens, and there are

>> no other blatant code execution avenues.

> 

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

> 

> But RPMB can also be used for example for restricting the

> number of PIN attempts.

> 

> A typical attack vector on phones (I think candybar phones

> even) was a robot that was punching PIN codes to unlock

> the phone, combined with an electronic probe that would

> cut the WE (write enable) signal to the flash right after

> punching a code. The counter was stored in the flash.

> 

> (A bit silly example as this can be countered by reading back

> the counter from flash and checking etc, but you get the idea,

> various versions of this attack is possible,)

> 

> With RPMB this can be properly protected against because

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

> monotonic counter has been increased.


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

> Of course the system can be compromised in other ways,

> (like, maybe it doesn't even have secure boot or even

> no encrypted drive) but this is one of the protection

> mechanisms that can plug one hole.


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

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

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

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

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

> It is thus a countermeasure to keyboard emulators and other

> evil hardware trying to brute force their way past screen

> locks and passwords. Such devices exist, sadly.


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

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub
Hector Martin March 11, 2021, 9:45 a.m. UTC | #17
On 11/03/2021 09.49, Linus Walleij wrote:
> The use case for TPM on laptops is similar: it can be used by a

> provider to lock down a machine, but it can also be used by the

> random user to store keys. Very few users beside James

> Bottomley are capable of doing that (I am not) but they exist.

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


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

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

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

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

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

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

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

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub
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:02 p.m. UTC | #20
On 11/03/2021 23.06, Linus Walleij wrote:
> Yes. And this is what mobile phone vendors typically did.

> 

> But the nature of different electrical attacks made them worried

> about different schemes involving cutting power and disturbing

> signals with different probes, so they wanted this counter

> implemented in hardware and that is why RPMB exists at all

> (IIUC).


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

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

> It is fine to be of the opinion that this entire piece of hardware

> is pointless because the same can be achieved using

> well written software.


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

> The position that the kernel community shall just ignore this

> hardware is a possible outcome of this discussion, but we need

> to have the discussion anyway, because now a RPMB framework

> is being promoted. The people who want it will need to sell it to

> us.


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

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

Consider this:

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

To read from your rollback-protected storage, you:

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

To write to your rollback protected storage, you:

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

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

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

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

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

>>> With RPMB this can be properly protected against because

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

>>> monotonic counter has been increased.

>>

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

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

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

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

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

> 

> This is true, I guess the argument is something along the

> line that if one link in the chain is weaker, why harden

> any other link, the chain will break anyway?


This is how security works, yes :-)

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

> I am more of the position let's harden this link if we can

> and then deal with the others when they come up, i.e.

> my concern is this piece of the puzzle, even if it is not

> the centerpiece (maybe the centerpiece is secure boot

> what do I know).


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

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

-- 
Hector Martin (marcan@marcan.st)
Public Key: https://mrcn.st/pub
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?
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
Sumit Garg March 12, 2021, 11:59 a.m. UTC | #24
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 | #25
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
diff mbox series

Patch

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__ */