summaryrefslogtreecommitdiff
path: root/drivers/crypto/inside-secure/safexcel_cipher.c
diff options
context:
space:
mode:
authorPascal van Leeuwen <pvanleeuwen@insidesecure.com>2019-07-02 17:39:54 +0300
committerHerbert Xu <herbert@gondor.apana.org.au>2019-07-26 07:51:59 +0300
commit5bdb6e6aa53bf5c6e6082a4bb44e1106a22d68b8 (patch)
tree9ac4dfef42f533c328c806b8f865110524233380 /drivers/crypto/inside-secure/safexcel_cipher.c
parent384ce433884af04c2063ec8a0ec0464b3fb6f1d2 (diff)
downloadlinux-5bdb6e6aa53bf5c6e6082a4bb44e1106a22d68b8.tar.xz
crypto: inside-secure - fix incorrect skcipher output IV
This patch fixes corruption issues with the skcipher output IV witnessed on x86+EIP197-FPGA (devboard). The original fix, commit 57660b11d5ad ("crypto: inside-secure - implement IV retrieval"), attempted to write out the result IV through the context record. However, this is not a reliable mechanism as there is no way of knowing the hardware context update actually arrived in memory, so it is possible to read the old contents instead of the updated IV. (and indeed, this failed for the x86/FPGA case) The alternative approach used here recognises the fact that the result IV for CBC is actually the last cipher block, which is the last input block in case of decryption and the last output block in case of encryption. So the result IV is taken from the input data buffer respectively the output data buffer instead, which *is* reliable. Signed-off-by: Pascal van Leeuwen <pvanleeuwen@verimatrix.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'drivers/crypto/inside-secure/safexcel_cipher.c')
-rw-r--r--drivers/crypto/inside-secure/safexcel_cipher.c84
1 files changed, 43 insertions, 41 deletions
diff --git a/drivers/crypto/inside-secure/safexcel_cipher.c b/drivers/crypto/inside-secure/safexcel_cipher.c
index 82f293caf68a..f03640cc2095 100644
--- a/drivers/crypto/inside-secure/safexcel_cipher.c
+++ b/drivers/crypto/inside-secure/safexcel_cipher.c
@@ -92,25 +92,6 @@ static void safexcel_skcipher_token(struct safexcel_cipher_ctx *ctx, u8 *iv,
token[0].instructions = EIP197_TOKEN_INS_LAST |
EIP197_TOKEN_INS_TYPE_CRYTO |
EIP197_TOKEN_INS_TYPE_OUTPUT;
-
- if (ctx->mode == CONTEXT_CONTROL_CRYPTO_MODE_CBC) {
- u32 last = (EIP197_MAX_TOKENS - 1) - offset;
-
- token[last].opcode = EIP197_TOKEN_OPCODE_CTX_ACCESS;
- token[last].packet_length = EIP197_TOKEN_DIRECTION_EXTERNAL |
- EIP197_TOKEN_EXEC_IF_SUCCESSFUL|
- EIP197_TOKEN_CTX_OFFSET(0x2);
- token[last].stat = EIP197_TOKEN_STAT_LAST_HASH |
- EIP197_TOKEN_STAT_LAST_PACKET;
- token[last].instructions =
- EIP197_TOKEN_INS_ORIGIN_LEN(block_sz / sizeof(u32)) |
- EIP197_TOKEN_INS_ORIGIN_IV0;
-
- /* Store the updated IV values back in the internal context
- * registers.
- */
- cdesc->control_data.control1 |= CONTEXT_CONTROL_CRYPTO_STORE;
- }
}
static void safexcel_aead_token(struct safexcel_cipher_ctx *ctx, u8 *iv,
@@ -348,6 +329,9 @@ static int safexcel_handle_req_result(struct safexcel_crypto_priv *priv, int rin
struct safexcel_cipher_req *sreq,
bool *should_complete, int *ret)
{
+ struct skcipher_request *areq = skcipher_request_cast(async);
+ struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(areq);
+ struct safexcel_cipher_ctx *ctx = crypto_skcipher_ctx(skcipher);
struct safexcel_result_desc *rdesc;
int ndesc = 0;
@@ -380,6 +364,18 @@ static int safexcel_handle_req_result(struct safexcel_crypto_priv *priv, int rin
dma_unmap_sg(priv->dev, dst, sg_nents(dst), DMA_FROM_DEVICE);
}
+ /*
+ * Update IV in req from last crypto output word for CBC modes
+ */
+ if ((!ctx->aead) && (ctx->mode == CONTEXT_CONTROL_CRYPTO_MODE_CBC) &&
+ (sreq->direction == SAFEXCEL_ENCRYPT)) {
+ /* For encrypt take the last output word */
+ sg_pcopy_to_buffer(dst, sg_nents(dst), areq->iv,
+ crypto_skcipher_ivsize(skcipher),
+ (cryptlen -
+ crypto_skcipher_ivsize(skcipher)));
+ }
+
*should_complete = true;
return ndesc;
@@ -392,6 +388,8 @@ static int safexcel_send_req(struct crypto_async_request *base, int ring,
unsigned int digestsize, u8 *iv, int *commands,
int *results)
{
+ struct skcipher_request *areq = skcipher_request_cast(base);
+ struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(areq);
struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(base->tfm);
struct safexcel_crypto_priv *priv = ctx->priv;
struct safexcel_command_desc *cdesc;
@@ -401,6 +399,19 @@ static int safexcel_send_req(struct crypto_async_request *base, int ring,
int nr_src, nr_dst, n_cdesc = 0, n_rdesc = 0, queued = totlen;
int i, ret = 0;
+ if ((!ctx->aead) && (ctx->mode == CONTEXT_CONTROL_CRYPTO_MODE_CBC) &&
+ (sreq->direction == SAFEXCEL_DECRYPT)) {
+ /*
+ * Save IV from last crypto input word for CBC modes in decrypt
+ * direction. Need to do this first in case of inplace operation
+ * as it will be overwritten.
+ */
+ sg_pcopy_to_buffer(src, sg_nents(src), areq->iv,
+ crypto_skcipher_ivsize(skcipher),
+ (totlen -
+ crypto_skcipher_ivsize(skcipher)));
+ }
+
if (src == dst) {
nr_src = dma_map_sg(priv->dev, src, sg_nents(src),
DMA_BIDIRECTIONAL);
@@ -570,7 +581,6 @@ static int safexcel_skcipher_handle_result(struct safexcel_crypto_priv *priv,
{
struct skcipher_request *req = skcipher_request_cast(async);
struct safexcel_cipher_req *sreq = skcipher_request_ctx(req);
- struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(async->tfm);
int err;
if (sreq->needs_inv) {
@@ -581,24 +591,6 @@ static int safexcel_skcipher_handle_result(struct safexcel_crypto_priv *priv,
err = safexcel_handle_req_result(priv, ring, async, req->src,
req->dst, req->cryptlen, sreq,
should_complete, ret);
-
- if (ctx->mode == CONTEXT_CONTROL_CRYPTO_MODE_CBC) {
- u32 block_sz = 0;
-
- switch (ctx->alg) {
- case SAFEXCEL_DES:
- block_sz = DES_BLOCK_SIZE;
- break;
- case SAFEXCEL_3DES:
- block_sz = DES3_EDE_BLOCK_SIZE;
- break;
- case SAFEXCEL_AES:
- block_sz = AES_BLOCK_SIZE;
- break;
- }
-
- memcpy(req->iv, ctx->base.ctxr->data, block_sz);
- }
}
return err;
@@ -656,12 +648,22 @@ static int safexcel_skcipher_send(struct crypto_async_request *async, int ring,
BUG_ON(!(priv->flags & EIP197_TRC_CACHE) && sreq->needs_inv);
- if (sreq->needs_inv)
+ if (sreq->needs_inv) {
ret = safexcel_cipher_send_inv(async, ring, commands, results);
- else
+ } else {
+ struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
+ u8 input_iv[AES_BLOCK_SIZE];
+
+ /*
+ * Save input IV in case of CBC decrypt mode
+ * Will be overwritten with output IV prior to use!
+ */
+ memcpy(input_iv, req->iv, crypto_skcipher_ivsize(skcipher));
+
ret = safexcel_send_req(async, ring, sreq, req->src,
- req->dst, req->cryptlen, 0, 0, req->iv,
+ req->dst, req->cryptlen, 0, 0, input_iv,
commands, results);
+ }
sreq->rdescs = *results;
return ret;