crypto: mxs-dcp - Fix AES issues
authorRadu Solea <radu.solea@nxp.com>
Tue, 2 Oct 2018 19:01:52 +0000 (19:01 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 24 Nov 2019 07:23:18 +0000 (08:23 +0100)
[ Upstream commit fadd7a6e616b89c7f4f7bfa7b824f290bab32c3c ]

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.solea@nxp.com>
Signed-off-by: Franck LENORMAND <franck.lenormand@nxp.com>
Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/crypto/mxs-dcp.c

index 4615dbee22d0a9dec5cba086e555901cd26a851b..e1e1e81107904f5304cff08956c1a0935a2edb4c 100644 (file)
@@ -225,6 +225,12 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
        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 |
@@ -254,6 +260,7 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
 
        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);
@@ -280,13 +287,15 @@ static int mxs_dcp_aes_block_crypt(struct crypto_async_request *arq)
 
        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;
 
@@ -305,6 +314,11 @@ 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)
@@ -321,13 +335,15 @@ 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);
@@ -350,6 +366,19 @@ static int mxs_dcp_aes_block_crypt(struct crypto_async_request *arq)
                                }
                        }
                } 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;