From: Radu Solea <radu.so...@nxp.com>

The DCP driver does not obey cryptlen, when doing android CTS this
results in passing to hardware input stream lengths which are not
multiple of block size.

Add a check to prevent future erroneous stream lengths from reaching the
hardware and adjust the scatterlist walking code to obey cryptlen.

Also properly copy-out the IV for chaining.

Signed-off-by: Radu Solea <radu.so...@nxp.com>
Signed-off-by: Franck LENORMAND <franck.lenorm...@nxp.com>
Signed-off-by: Leonard Crestez <leonard.cres...@nxp.com>
---
 drivers/crypto/mxs-dcp.c | 33 +++++++++++++++++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

diff --git a/drivers/crypto/mxs-dcp.c b/drivers/crypto/mxs-dcp.c
index 3821cf971b5e..203c682dda99 100644
--- a/drivers/crypto/mxs-dcp.c
+++ b/drivers/crypto/mxs-dcp.c
@@ -230,10 +230,16 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
        dma_addr_t src_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_in_buf,
                                             DCP_BUF_SZ, DMA_TO_DEVICE);
        dma_addr_t dst_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_out_buf,
                                             DCP_BUF_SZ, DMA_FROM_DEVICE);
 
+       if (actx->fill % AES_BLOCK_SIZE) {
+               dev_err(sdcp->dev, "Invalid block size!\n");
+               ret = -EINVAL;
+               goto aes_done_run;
+       }
+
        /* Fill in the DMA descriptor. */
        desc->control0 = MXS_DCP_CONTROL0_DECR_SEMAPHORE |
                    MXS_DCP_CONTROL0_INTERRUPT |
                    MXS_DCP_CONTROL0_ENABLE_CIPHER;
 
@@ -259,10 +265,11 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
        desc->payload = key_phys;
        desc->status = 0;
 
        ret = mxs_dcp_start_dma(actx);
 
+aes_done_run:
        dma_unmap_single(sdcp->dev, key_phys, 2 * AES_KEYSIZE_128,
                         DMA_TO_DEVICE);
        dma_unmap_single(sdcp->dev, src_phys, DCP_BUF_SZ, DMA_TO_DEVICE);
        dma_unmap_single(sdcp->dev, dst_phys, DCP_BUF_SZ, DMA_FROM_DEVICE);
 
@@ -285,17 +292,19 @@ static int mxs_dcp_aes_block_crypt(struct 
crypto_async_request *arq)
        uint8_t *in_buf = sdcp->coh->aes_in_buf;
        uint8_t *out_buf = sdcp->coh->aes_out_buf;
 
        uint8_t *out_tmp, *src_buf, *dst_buf = NULL;
        uint32_t dst_off = 0;
+       uint32_t last_out_len = 0;
 
        uint8_t *key = sdcp->coh->aes_key;
 
        int ret = 0;
        int split = 0;
-       unsigned int i, len, clen, rem = 0;
+       unsigned int i, len, clen, rem = 0, tlen = 0;
        int init = 0;
+       bool limit_hit = false;
 
        actx->fill = 0;
 
        /* Copy the key from the temporary location. */
        memcpy(key, actx->key, actx->key_len);
@@ -310,10 +319,15 @@ static int mxs_dcp_aes_block_crypt(struct 
crypto_async_request *arq)
        }
 
        for_each_sg(req->src, src, nents, i) {
                src_buf = sg_virt(src);
                len = sg_dma_len(src);
+               tlen += len;
+               limit_hit = tlen > req->nbytes;
+
+               if (limit_hit)
+                       len = req->nbytes - (tlen - len);
 
                do {
                        if (actx->fill + len > out_off)
                                clen = out_off - actx->fill;
                        else
@@ -326,17 +340,19 @@ static int mxs_dcp_aes_block_crypt(struct 
crypto_async_request *arq)
 
                        /*
                         * If we filled the buffer or this is the last SG,
                         * submit the buffer.
                         */
-                       if (actx->fill == out_off || sg_is_last(src)) {
+                       if (actx->fill == out_off || sg_is_last(src) ||
+                               limit_hit) {
                                ret = mxs_dcp_run_aes(actx, req, init);
                                if (ret)
                                        return ret;
                                init = 0;
 
                                out_tmp = out_buf;
+                               last_out_len = actx->fill;
                                while (dst && actx->fill) {
                                        if (!split) {
                                                dst_buf = sg_virt(dst);
                                                dst_off = 0;
                                        }
@@ -355,10 +371,23 @@ static int mxs_dcp_aes_block_crypt(struct 
crypto_async_request *arq)
                                                split = 1;
                                        }
                                }
                        }
                } while (len);
+
+               if (limit_hit)
+                       break;
+       }
+
+       /* Copy the IV for CBC for chaining */
+       if (!rctx->ecb) {
+               if (rctx->enc)
+                       memcpy(req->info, out_buf+(last_out_len-AES_BLOCK_SIZE),
+                               AES_BLOCK_SIZE);
+               else
+                       memcpy(req->info, in_buf+(last_out_len-AES_BLOCK_SIZE),
+                               AES_BLOCK_SIZE);
        }
 
        return ret;
 }
 
-- 
2.17.1

Reply via email to