// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2012-2019 ARM Limited (or its affiliates). */
#include <linux/kernel.h>
#include <linux/module.h>
#include <crypto/algapi.h>
#include <crypto/internal/skcipher.h>
#include <crypto/internal/des.h>
#include <crypto/xts.h>
#include <crypto/sm4.h>
#include <crypto/scatterwalk.h>
#include "cc_driver.h"
#include "cc_lli_defs.h"
#include "cc_buffer_mgr.h"
#include "cc_cipher.h"
#include "cc_request_mgr.h"
#define MAX_SKCIPHER_SEQ_LEN 6
#define template_skcipher template_u.skcipher
struct cc_user_key_info {
u8 *key;
dma_addr_t key_dma_addr;
};
struct cc_hw_key_info {
enum cc_hw_crypto_key key1_slot;
enum cc_hw_crypto_key key2_slot;
};
struct cc_cpp_key_info {
u8 slot;
enum cc_cpp_alg alg;
};
enum cc_key_type {
CC_UNPROTECTED_KEY, /* User key */
CC_HW_PROTECTED_KEY, /* HW (FDE) key */
CC_POLICY_PROTECTED_KEY, /* CPP key */
CC_INVALID_PROTECTED_KEY /* Invalid key */
};
struct cc_cipher_ctx {
struct cc_drvdata *drvdata;
int keylen;
int cipher_mode;
int flow_mode;
unsigned int flags;
enum cc_key_type key_type;
struct cc_user_key_info user;
union {
struct cc_hw_key_info hw;
struct cc_cpp_key_info cpp;
};
struct crypto_shash *shash_tfm;
struct crypto_skcipher *fallback_tfm;
bool fallback_on;
};
static void cc_cipher_complete(struct device *dev, void *cc_req, int err);
static inline enum cc_key_type cc_key_type(struct crypto_tfm *tfm)
{
struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
return ctx_p->key_type;
}
static int validate_keys_sizes(struct cc_cipher_ctx *ctx_p, u32 size)
{
switch (ctx_p->flow_mode) {
case S_DIN_to_AES:
switch (size) {
case CC_AES_128_BIT_KEY_SIZE:
case CC_AES_192_BIT_KEY_SIZE:
if (ctx_p->cipher_mode != DRV_CIPHER_XTS)
return 0;
break;
case CC_AES_256_BIT_KEY_SIZE:
return 0;
case (CC_AES_192_BIT_KEY_SIZE * 2):
case (CC_AES_256_BIT_KEY_SIZE * 2):
if (ctx_p->cipher_mode == DRV_CIPHER_XTS ||
ctx_p->cipher_mode == DRV_CIPHER_ESSIV)
return 0;
break;
default:
break;
}
break;
case S_DIN_to_DES:
if (size == DES3_EDE_KEY_SIZE || size == DES_KEY_SIZE)
return 0;
break;
case S_DIN_to_SM4:
if (size == SM4_KEY_SIZE)
return 0;
break;
default:
break;
}
return -EINVAL;
}
static int validate_data_size(struct cc_cipher_ctx *ctx_p,
unsigned int size)
{
switch (ctx_p->flow_mode) {
case S_DIN_to_AES:
switch (ctx_p->cipher_mode) {
case DRV_CIPHER_XTS:
case DRV_CIPHER_CBC_CTS:
if (size >= AES_BLOCK_SIZE)
return 0;
break;
case DRV_CIPHER_OFB:
case DRV_CIPHER_CTR:
return 0;
case DRV_CIPHER_ECB:
case DRV_CIPHER_CBC:
case DRV_CIPHER_ESSIV:
if (IS_ALIGNED(size, AES_BLOCK_SIZE))
return 0;
break;
default:
break;
}
break;
case S_DIN_to_DES:
if (IS_ALIGNED(size, DES_BLOCK_SIZE))
return 0;
break;
case S_DIN_to_SM4:
switch (ctx_p->cipher_mode) {
case DRV_CIPHER_CTR:
return 0;
case DRV_CIPHER_ECB:
case DRV_CIPHER_CBC:
if (IS_ALIGNED(size, SM4_BLOCK_SIZE))
return 0;
break;
default:
break;
}
break;
default:
break;
}
return -EINVAL;
}
static int cc_cipher_init(struct crypto_tfm *tfm)
{
struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
struct cc_crypto_alg *cc_alg =
container_of(tfm->__crt_alg, struct cc_crypto_alg,
skcipher_alg.base);
struct device *dev = drvdata_to_dev(cc_alg->drvdata);
unsigned int max_key_buf_size = cc_alg->skcipher_alg.max_keysize;
unsigned int fallback_req_size = 0;
dev_dbg(dev, "Initializing context @%p for %s\n", ctx_p,
crypto_tfm_alg_name(tfm));
ctx_p->cipher_mode = cc_alg->cipher_mode;
ctx_p->flow_mode = cc_alg->flow_mode;
ctx_p->drvdata = cc_alg->drvdata;
if (ctx_p->cipher_mode == DRV_CIPHER_ESSIV) {
const char *name = crypto_tfm_alg_name(tfm);
/* Alloc hash tfm for essiv */
ctx_p->shash_tfm = crypto_alloc_shash("sha256", 0, 0);
if (IS_ERR(ctx_p->shash_tfm)) {
dev_err(dev, "Error allocating hash tfm for ESSIV.\n");
return PTR_ERR(ctx_p->shash_tfm);
}
max_key_buf_size <<= 1;
/* Alloc fallabck tfm or essiv when key size != 256 bit */
ctx_p->fallback_tfm =
crypto_alloc_skcipher(name, 0, CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC);
if (IS_ERR(ctx_p->fallback_tfm)) {
/* Note we're still allowing registration with no fallback since it's
* better to have most modes supported than none at all.
*/
dev_warn(dev, "Error allocating fallback algo %s. Some modes may be available.\n",
name);
ctx_p->fallback_tfm = NULL;
} else {
fallback_req_size = crypto_skcipher_reqsize(ctx_p->fallback_tfm);
}
}
crypto_skcipher_set_reqsize(__crypto_skcipher_cast(tfm),
sizeof(struct cipher_req_ctx) + fallback_req_size);
/* Allocate key buffer, cache line aligned */
ctx_p->user.key = kzalloc(max_key_buf_size, GFP_KERNEL);
if (!ctx_p->user.key)
goto free_fallback;
dev_dbg(dev, "Allocated key buffer in context. key=@%p\n",
ctx_p->user.key);
/* Map key buffer */
ctx_p->user.key_dma_addr = dma_map_single(dev, ctx_p->user.key,
max_key_buf_size,
DMA_TO_DEVICE);
if (dma_mapping_error(dev, ctx_p->user.key_dma_addr)) {
dev_err(dev, "Mapping Key %u B at va=%pK for DMA failed\n",
max_key_buf_size, ctx_p->user.key);
goto free_key;
}
dev_dbg(dev, "Mapped key %u B at va=%pK to dma=%pad\n",
max_key_buf_size, ctx_p->user.key, &ctx_p->user.key_dma_addr);
return 0;
free_key:
kfree(ctx_p->user.key);
free_fallback:
crypto_free_skcipher(ctx_p->fallback_tfm);
crypto_free_shash(ctx_p->shash_tfm);
return -ENOMEM;
}
static void cc_cipher_exit(struct crypto_tfm *tfm)
{
struct crypto_alg *alg = tfm->__crt_alg;
struct cc_crypto_alg *cc_alg =
container_of(alg, struct cc_crypto_alg,
skcipher_alg.base);
unsigned int max_key_buf_size = cc_alg->skcipher_alg.max_keysize;
struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
struct device *dev = drvdata_to_dev(ctx_p->drvdata);
dev_dbg(dev, "Clearing context @%p for %s\n",
crypto_tfm_ctx(tfm), crypto_tfm_alg_name(tfm));
if (ctx_p->cipher_mode == DRV_CIPHER_ESSIV) {
/* Free hash tfm for essiv */
crypto_free_shash(ctx_p->shash_tfm);
ctx_p->shash_tfm = NULL;
crypto_free_skcipher(ctx_p->fallback_tfm);
ctx_p->fallback_tfm = NULL;
}
/* Unmap key buffer */
dma_unmap_single(dev, ctx_p->user.key_dma_addr, max_key_buf_size,
DMA_TO_DEVICE);
dev_dbg(dev, "Unmapped key buffer key_dma_addr=%pad\n",
&ctx_p->user.key_dma_addr);
/* Free key buffer in context */
kfree_sensitive(ctx_p->user.key);
dev_dbg(dev, "Free key buffer in context. key=@%p\n", ctx_p->user.key);
}
struct tdes_keys {
u8 key1[DES_KEY_SIZE];
u8 key2[DES_KEY_SIZE];
u8 key3[DES_KEY_SIZE];
};
static enum cc_hw_crypto_key cc_slot_to_hw_key(u8 slot_num)
{
switch (slot_num) {
case 0:
return KFDE0_KEY;
case 1:
return KFDE1_KEY;
case 2:
return KFDE2_KEY;
case 3:
return KFDE3_KEY;
}
return END_OF_KEYS;
}
static u8 cc_slot_to_cpp_key(u8 slot_num)
{
return (slot_num - CC_FIRST_CPP_KEY_SLOT);
}
static inline enum cc_key_type cc_slot_to_key_type(u8 slot_num)
{
if (slot_num >= CC_FIRST_HW_KEY_SLOT && slot_num <= CC_LAST_HW_KEY_SLOT)
return CC_HW_PROTECTED_KEY;
else if (slot_num >= CC_FIRST_CPP_KEY_SLOT &&
slot_num <= CC_LAST_CPP_KEY_SLOT)
return CC_POLICY_PROTECTED_KEY;
else
return CC_INVALID_PROTECTED_KEY;
}
static int cc_cipher_sethkey(struct crypto_skcipher *sktfm, const u8 *key,
unsigned int keylen)
{
struct crypto_tfm *tfm = crypto_skcipher_tfm(sktfm);
struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
struct device *dev = drvdata_to_dev(ctx_p->drvdata);
struct cc_hkey_info hki;
dev_dbg(dev, "Setting HW key in context @%p for %s. keylen=%u\n",
ctx_p, crypto_tfm_alg_name(tfm), keylen);
dump_byte_array("key", key, keylen);
/* STAT_PHASE_0: Init and sanity checks */
/* This check the size of the protected key token */
if (keylen != sizeof(hki)) {
dev_err(dev, "Unsu
|