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