Commit dcaac549 authored by Aleksander Morgado's avatar Aleksander Morgado Committed by Ezequiel Garcia
Browse files

caam: new caamblob driver



This driver allows userspace applications to use the blob
encapsulation and decapsulation capabilities of the CAAM module, using
the secure memory keystore driver (sm_store).

The sm_store driver is designed to work only with SECURE MEMORY.

The blobs are always encapsulated and decapsulated from/to secure
memory, as the implementation in sm_store doesn't allow working with
blobs generated from general memory. All blobs generated by this
driver are secure memory blobs, and therefore the access permissions
given in the SMAPJR and SMAGR registers are included in the blob
itself. Also, the key modifier used in the process can just be 8
bytes (instead of the 16 bytes required for general memory blobs).

The maximum size of the input data that can be used to generate a blob
is given by the slot size (configurable via
CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE).

Even if the sm_store is designed to assume that the input data is a
key, this driver treats the input data as general data. The driver
always generates 'RED' blobs (i.e. never BLACK key blobs). The data
loaded from a encapsulated blob does anyway not stay in secure memory
once the ioctl() has finished, so it really does not matter.

The encapsulation and decapsulation commands through the device
ioctl() are independent from each other. If more than one such
commands happen at the same time, their success will depend on whether
the secure storage was able to allocate a slot for the operation.

This driver is somewhat based on the sm_test driver.
Signed-off-by: default avatarAleksander Morgado <aleksander@aleksander.es>
Signed-off-by: Lucas Stach's avatarLucas Stach <l.stach@pengutronix.de>
parent 8246874e
......@@ -157,7 +157,7 @@ config CRYPTO_DEV_FSL_CAAM_SM
config CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE
int "Size of each keystore slot in Secure Memory"
depends on CRYPTO_DEV_FSL_CAAM_SM
range 5 9
range 5 11
default 7
help
Select size of allocation units to divide Secure Memory pages into
......@@ -169,6 +169,16 @@ config CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE
7 => 128 bytes
8 => 256 bytes
9 => 512 bytes
10 => 1024 bytes
11 => 2048 bytes
config CRYPTO_DEV_FSL_CAAM_BLOB
tristate "CAAM Secure Memory - Blob management (EXPERIMENTAL)"
depends on CRYPTO_DEV_FSL_CAAM_SM
default n
help
Support for encapsulation and decapsulation of blobs using a misc
device.
endif # CRYPTO_DEV_FSL_CAAM_JR
......
......@@ -14,6 +14,7 @@ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_JR) += caam_jr.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC) += caamalg_desc.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API_DESC) += caamhash_desc.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SM) += sm_store.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_BLOB) += caamblob.o
caam-y := ctrl.o
caam_jr-y := jr.o key_gen.o
......
/*
* CAAM blob management module
* Copyright (C) 2016 Zodiac Inflight Innovations. All Rights Reserved.
*
* This driver allows userspace applications to use the blob encapsulation
* and decapsulation capabilities of the CAAM module, using the secure
* memory keystore driver (sm_store).
*
* The sm_store driver is designed to work only with SECURE MEMORY.
*
* The blobs are always encapsulated and decapsulated from/to secure memory,
* as the implementation in sm_store doesn't allow working with blobs
* generated from general memory. All blobs generated by this driver are
* secure memory blobs, and therefore the access permissions given in the
* SMAPJR and SMAGR registers are included in the blob itself. Also, the
* key modifier used in the process can just be 8 bytes (instead of the 16
* bytes required for general memory blobs)..
*
* The maximum size of the input data that can be used to generate a blob is
* given by the slot size (configurable via CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE).
*
* Even if the sm_store is designed to assume that the input data is a key,
* this driver treats the input data as general data. The driver always
* generates 'RED' blobs (i.e. never BLACK key blobs). The data loaded from
* a encapsulated blob does anyway not stay in secure memory once the ioctl()
* has finished, so it really does not matter.
*
* The encapsulation and decapsulation commands through the device ioctl()
* are independent from each other. If more than one such commands happen
* at the same time, their success will depend on whether the secure storage
* was able to allocate a slot for the operation.
*
* This driver is somewhat based on the sm_test driver.
*/
/* #define DEBUG */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/compat.h>
#include <linux/printk.h>
#include <crypto/caamblob.h>
#include "regs.h"
#include "compat.h"
#include "intern.h"
#include "sm.h"
/*
* MX6 bootloader stores some stuff in unit 0, so let's
* use 1 or above.
*/
#define DEFAULT_UNIT 1
/******************************************************************************/
/* Global driver data.
*
* It gets allocated and initialized when the module is loaded; it gets freed
* when the module is unloaded.
*
* The global driver data keeps a reference to the security device node.
*/
struct caam_blob_data {
struct device_node *dev_node;
struct platform_device *pdev;
struct device *ctrldev;
struct caam_drv_private *ctrlpriv;
struct device *smdev;
struct caam_drv_private_sm *smpriv;
};
static struct caam_blob_data *data;
static int caam_blob_data_init(void)
{
int ret;
u32 units;
/* Allocate data */
data = kmalloc(sizeof(struct caam_blob_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
if (!data->dev_node) {
data->dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
if (!data->dev_node) {
pr_err("caamblob: no compatible node found");
ret = -ENODEV;
goto out_free;
}
}
pr_debug("caamblob: compatible node found");
/* acquires a reference to pdev */
data->pdev = of_find_device_by_node(data->dev_node);
if (!data->pdev) {
pr_err("caamblob: no compatible device found");
ret = -ENODEV;
goto out_put_free;
}
pr_debug("caamblob: compatible device found");
data->ctrldev = &data->pdev->dev;
data->ctrlpriv = dev_get_drvdata(data->ctrldev);
if (!data->ctrlpriv) {
pr_err("caamblob: caam driver data unavailable");
ret = -ENODEV;
goto out_put_free;
}
data->smdev = data->ctrlpriv->smdev;
data->smpriv = dev_get_drvdata(data->smdev);
if (data->smpriv == NULL) {
pr_err("caamblob: secure memory driver data unavailable");
ret = -ENODEV;
goto out_put_free;
}
#ifdef DEBUG
{
u32 page;
pr_debug("caamblob: max pages: %d\n", data->smpriv->max_pages);
pr_debug("caamblob: top partition: %d\n", data->smpriv->top_partition);
pr_debug("caamblob: top page: %d\n", data->smpriv->top_page);
pr_debug("caamblob: page size: %d\n", data->smpriv->page_size);
pr_debug("caamblob: total: %d\n", data->smpriv->page_size * data->smpriv->top_page);
pr_debug("caamblob: slot size: %d\n", data->smpriv->slot_size);
pr_debug("caamblob: pages owned: %d", data->smpriv->localpages);
for (page = 0; page < data->smpriv->localpages; page++) {
pr_debug("caamblob [unit %u] physical page: %d, owning partition: %d",
page,
data->smpriv->pagedesc[page].phys_pagenum,
data->smpriv->pagedesc[page].own_part);
}
}
#endif
/* Check available keystores */
units = sm_detect_keystore_units(data->smdev);
if (!units)
pr_err("caamblob: no keystore units available");
if (units < (DEFAULT_UNIT + 1)) {
pr_err("caamblob: insufficient keystore units");
ret = -ENODEV;
goto out_put_free;
}
/* Initialize/Establish Keystore */
sm_establish_keystore(data->smdev, DEFAULT_UNIT);
return 0;
out_put_free:
of_node_put(data->dev_node);
out_free:
kfree(data);
return ret;
}
static void caam_blob_data_shutdown(void)
{
sm_release_keystore(data->smdev, DEFAULT_UNIT);
of_node_put(data->dev_node);
kfree(data);
}
/******************************************************************************/
/* kernel-internal extension to struct caam_blob_query */
struct kernel_caam_blob_query {
struct caam_blob_query op;
};
static int caam_blob_query(struct kernel_caam_blob_query *kop)
{
/* Maximum slot size is defined at compile time */
kop->op.max_src_len = 1 << CONFIG_CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE;
return 0;
}
/******************************************************************************/
/* kernel-internal extension to struct caam_blob_operation */
struct kernel_caam_blob_op {
struct caam_blob_op op;
};
static int camm_blob_encapsulation(struct kernel_caam_blob_op *kop)
{
u32 keyslot = 0;
int st;
u8 *blob, *src, keymod[8];
u32 blob_size;
/* pull keymod from userspace */
if (copy_from_user(keymod, kop->op.keymod, 8))
return -EFAULT;
/* The input data must fit in one single slot */
if (kop->op.src_len > data->smpriv->slot_size) {
pr_debug("caamblob: input data (%u bytes) doesn't fit in 1 slot "
"(%u bytes)", kop->op.src_len, data->smpriv->slot_size);
return -EINVAL;
}
/* The output buffer should have BLOB_OVERHEAD bytes more than
* the input buffer */
blob_size = kop->op.src_len + BLOB_OVERHEAD;
if (kop->op.dst_len < blob_size) {
pr_debug("caamblob: output buffer (%u bytes) is not enough "
"(%u bytes needed)", kop->op.dst_len, blob_size);
return -ENOSPC;
}
/* Allocate DMA-able memory for the blob export */
blob = kzalloc(blob_size, GFP_KERNEL | GFP_DMA);
if (blob == NULL)
return -ENOMEM;
/* Allocate kernel memory for the input data */
src = kzalloc(kop->op.src_len, GFP_KERNEL);
if (src == NULL) {
st = -EFAULT;
goto freemem;
}
/* Copy the input data into kernel memory */
if (copy_from_user(src, kop->op.src, kop->op.src_len)) {
st = -EFAULT;
goto freemem;
}
/* Allocate slot in secure memory; padded to the next AES block size.
* May fail if there are no slots available. */
if (sm_keystore_slot_alloc(data->smdev,
DEFAULT_UNIT,
AES_BLOCK_PAD(kop->op.src_len),
&keyslot)) {
pr_warning("caamblob: slot allocation failed");
st = -EBUSY;
goto freemem;
}
/* Load input into the newly allocated slot. The input data does NOT
* need to be in DMA-able memory. */
if (sm_keystore_slot_load(data->smdev,
DEFAULT_UNIT,
keyslot,
src,
kop->op.src_len)) {
pr_warning("caamblob: slot loading failed");
st = -EFAULT;
goto dealloc;
}
/* Export key into DMA-able memory */
if (sm_keystore_slot_export(data->smdev,
DEFAULT_UNIT,
keyslot,
RED_KEY,
KEY_COVER_ECB,
blob,
kop->op.src_len,
keymod)) {
pr_warning("caamblob: slot export failed");
st = -EFAULT;
goto dealloc;
}
/* Copy the blob back to the ioctl() struct */
st = copy_to_user(kop->op.dst, blob, blob_size);
kop->op.dst_len = blob_size;
dealloc:
sm_keystore_slot_dealloc(data->smdev, DEFAULT_UNIT, keyslot);
freemem:
kfree(src);
kfree(blob);
return st;
}
static int camm_blob_decapsulation(struct kernel_caam_blob_op *kop)
{
u32 keyslot = 0;
int st;
u8 *blob, *dst, keymod[8];
u32 data_size;
/* The input buffer should be an encapsulated key (at least 1 byte plus blob) */
if (kop->op.src_len < (BLOB_OVERHEAD + 1)) {
pr_debug("caamblob: invalid blob input");
return -EINVAL;
}
/* The decapsulated data should fit in one single slot */
data_size = kop->op.src_len - BLOB_OVERHEAD;
if (data_size > data->smpriv->slot_size) {
pr_debug("caamblob: input blob decapsulated data (%u bytes) doesn't fit"
"in 1 slot (%u bytes)", data_size, data->smpriv->slot_size);
return -EINVAL;
}
/* The output buffer should have enough space for the whole key */
if (kop->op.dst_len < data_size) {
pr_debug("caamblob: output buffer (%u bytes) is not enough "
"(%u bytes needed)", kop->op.dst_len, data_size);
return -ENOSPC;
}
/* pull keymod from userspace */
if (copy_from_user(keymod, kop->op.keymod, 8))
return -EFAULT;
/* Allocate DMA-able memory for the input blob */
blob = kzalloc(kop->op.src_len, GFP_KERNEL | GFP_DMA);
if (blob == NULL)
return -ENOMEM;
/* Allocate kernel memory for the output data */
dst = kzalloc(data_size, GFP_KERNEL);
if (dst == NULL) {
st = -EFAULT;
goto freemem;
}
/* Copy the input blob into DMA-able memory */
if (copy_from_user(blob, kop->op.src, kop->op.src_len)) {
st = -EFAULT;
goto freemem;
}
/* Allocate slot in secure memory; padded to the next AES block size */
if (sm_keystore_slot_alloc(data->smdev,
DEFAULT_UNIT,
AES_BLOCK_PAD(data_size),
&keyslot)) {
pr_warning("caamblob: slot allocation failed");
st = -EBUSY;
goto freemem;
}
/* Import key. The input data NEEDS to be in DMA-able memory. */
if (sm_keystore_slot_import(data->smdev,
DEFAULT_UNIT,
keyslot,
RED_KEY,
KEY_COVER_ECB,
blob,
data_size,
keymod)) {
pr_warning("caamblob: slot import failed");
st = -EFAULT;
goto dealloc;
}
/* Read the key into kernel memory */
if (sm_keystore_slot_read(data->smdev,
DEFAULT_UNIT,
keyslot,
data_size,
dst)) {
pr_warning("caamblob: slot reading failed");
st = -EFAULT;
goto dealloc;
}
/* Set the output size */
st = copy_to_user(kop->op.dst, dst, data_size);
kop->op.dst_len = data_size;
dealloc:
sm_keystore_slot_dealloc(data->smdev, DEFAULT_UNIT, keyslot);
freemem:
kfree(dst);
kfree(blob);
return st;
}
static int
caam_blob_open(struct inode *inode, struct file *f)
{
return 0;
}
static int
caam_blob_release(struct inode *inode, struct file *f)
{
return 0;
}
static long caam_blob_ioctl_op(struct file *f, unsigned int cmd,
unsigned long arg_)
{
void __user *arg = (void __user *)arg_;
struct kernel_caam_blob_op kop;
int st;
if (unlikely(copy_from_user(&kop.op, arg, sizeof(kop.op)))) {
pr_err("caamblob: error copying from userspace");
return -EFAULT;
}
switch (cmd) {
case CBIOENC:
if ((st = camm_blob_encapsulation(&kop)) != 0)
return st;
break;
case CBIODEC:
if ((st = camm_blob_decapsulation(&kop)) != 0)
return st;
break;
default:
return -EFAULT;
}
if (unlikely(copy_to_user(arg, &kop.op, sizeof(kop.op)))) {
pr_err("caamblob: error copying to userspace");
return -EFAULT;
}
return 0;
}
static long caam_blob_ioctl_query(struct file *f, unsigned int cmd,
unsigned long arg_)
{
void __user *arg = (void __user *)arg_;
struct kernel_caam_blob_query kop;
int st;
if (unlikely(copy_from_user(&kop.op, arg, sizeof(kop.op)))) {
pr_err("caamblob: error copying from userspace");
return -EFAULT;
}
if ((st = caam_blob_query (&kop)) != 0)
return st;
if (unlikely(copy_to_user(arg, &kop.op, sizeof(kop.op)))) {
pr_err("caamblob: error copying to userspace");
return -EFAULT;
}
return 0;
}
static long caam_blob_ioctl(struct file *f, unsigned int cmd,
unsigned long arg_)
{
switch (cmd) {
case CBIOENC:
case CBIODEC:
return caam_blob_ioctl_op (f, cmd, arg_);
case CBIOQRY:
return caam_blob_ioctl_query (f, cmd, arg_);
default:
pr_warning("caamblob: unknown ioctl() command: %u", cmd);
return -EINVAL;
}
}
#ifdef CONFIG_COMPAT
static long caam_blob_compat_ioctl(struct file *f, unsigned int ioctl,
unsigned long arg)
{
return caam_blob_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
}
#endif
struct file_operations caam_blob_fops = {
.owner = THIS_MODULE,
.open = caam_blob_open,
.release = caam_blob_release,
.unlocked_ioctl = caam_blob_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = caam_blob_compat_ioctl,
#endif
};
struct miscdevice caam_blob = {
.minor = CAAM_BLOB_MINOR,
.name = "caam-blob",
.fops = &caam_blob_fops,
};
static int __init caam_blob_init(void)
{
int rc;
pr_debug("caamblob: module loading...");
/* Look for a compatible device and initialize data */
rc = caam_blob_data_init();
if (rc != 0) {
pr_err("caamblob: initialization failed");
return rc;
}
/* If there's a compatible device, register misc device */
rc = misc_register(&caam_blob);
if (rc != 0) {
pr_err("caamblob: registration of caam blob device failed");
caam_blob_data_shutdown();
return rc;
}
pr_debug("caamblob: module loaded");
return 0;
}
static void __exit caam_blob_exit(void)
{
misc_deregister(&caam_blob);
caam_blob_data_shutdown();
pr_debug("caamblob: module unloaded");
}
module_init(caam_blob_init);
module_exit(caam_blob_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("CAAM blob management module");
MODULE_AUTHOR("Aleksander Morgado <aleksander@aleksander.es>");
/*
* CAAM blob management module
* Copyright (C) 2016 Zodiac Inflight Innovations. All Rights Reserved.
*/
#ifndef _CAAM_BLOB_H
#define _CAAM_BLOB_H
#include <linux/types.h>
#ifndef __KERNEL__
#define __user
#endif
#define CAAM_BLOB_MINOR MISC_DYNAMIC_MINOR
/*
* CBIOENC/CBIODEC operation.
*
* The dst_len parameter acts as both input and output. In the input,
* it should specify the size of the output buffer. In the output, it
* contains tha actual amount of data that was written to the output
* buffer.
*/
struct caam_blob_op {
__u8 __user *keymod; /* (in) pointer to the 8-byte key modifier */
__u32 src_len; /* (in) length of source data */
__u8 __user *src; /* (in) source data */
__u32 dst_len; /* (in/out) length of output buffer/data */
__u8 __user *dst; /* (out) pointer to output data */
};
/* Encapsulation */
#define CBIOENC _IOWR('c', 101, struct caam_blob_op)
/* Decapsulation */
#define CBIODEC _IOWR('c', 102, struct caam_blob_op)
/*
* CBIOQRY operation
*/
struct caam_blob_query {
__u32 max_src_len; /* (out) the maximum length allowed for input data */
};
/* Query */
#define CBIOQRY _IOWR('c', 103, struct caam_blob_query)
#endif /* _CAAM_BLOB_H */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment