]> git.baikalelectronics.ru Git - kernel.git/commitdiff
crypto: mxs-dcp - Fix AES issues
authorRadu Solea <radu.solea@nxp.com>
Tue, 2 Oct 2018 19:01:52 +0000 (19:01 +0000)
committerHerbert Xu <herbert@gondor.apana.org.au>
Mon, 8 Oct 2018 05:45:41 +0000 (13:45 +0800)
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>
drivers/crypto/mxs-dcp.c

index 189bf0a45649d521bd42f030f167aaab25217824..aaa95c09f06830ba69e5395500ae5c5370a5b806 100644 (file)
@@ -230,6 +230,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 |
@@ -259,6 +265,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);
@@ -285,13 +292,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;
 
@@ -310,6 +319,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)
@@ -326,13 +340,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);
@@ -355,6 +371,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;