crypto: ixp4xx - update IV after requests
[linux-2.6-block.git] / drivers / crypto / ixp4xx_crypto.c
index ed3deaa5ed2b835b1a3e04f742777abc6ee2767a..f577ee4afd06f031925113e898f207bcdd6a9bf9 100644 (file)
@@ -149,6 +149,8 @@ struct crypt_ctl {
 struct ablk_ctx {
        struct buffer_desc *src;
        struct buffer_desc *dst;
+       u8 iv[MAX_IVLEN];
+       bool encrypt;
 };
 
 struct aead_ctx {
@@ -381,6 +383,20 @@ static void one_packet(dma_addr_t phys)
        case CTL_FLAG_PERFORM_ABLK: {
                struct skcipher_request *req = crypt->data.ablk_req;
                struct ablk_ctx *req_ctx = skcipher_request_ctx(req);
+               struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+               unsigned int ivsize = crypto_skcipher_ivsize(tfm);
+               unsigned int offset;
+
+               if (ivsize > 0) {
+                       offset = req->cryptlen - ivsize;
+                       if (req_ctx->encrypt) {
+                               scatterwalk_map_and_copy(req->iv, req->dst,
+                                                        offset, ivsize, 0);
+                       } else {
+                               memcpy(req->iv, req_ctx->iv, ivsize);
+                               memzero_explicit(req_ctx->iv, ivsize);
+                       }
+               }
 
                if (req_ctx->dst) {
                        free_buf_chain(dev, req_ctx->dst, crypt->dst_buf);
@@ -876,6 +892,7 @@ static int ablk_perform(struct skcipher_request *req, int encrypt)
        struct ablk_ctx *req_ctx = skcipher_request_ctx(req);
        struct buffer_desc src_hook;
        struct device *dev = &pdev->dev;
+       unsigned int offset;
        gfp_t flags = req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ?
                                GFP_KERNEL : GFP_ATOMIC;
 
@@ -885,6 +902,7 @@ static int ablk_perform(struct skcipher_request *req, int encrypt)
                return -EAGAIN;
 
        dir = encrypt ? &ctx->encrypt : &ctx->decrypt;
+       req_ctx->encrypt = encrypt;
 
        crypt = get_crypt_desc();
        if (!crypt)
@@ -900,6 +918,10 @@ static int ablk_perform(struct skcipher_request *req, int encrypt)
 
        BUG_ON(ivsize && !req->iv);
        memcpy(crypt->iv, req->iv, ivsize);
+       if (ivsize > 0 && !encrypt) {
+               offset = req->cryptlen - ivsize;
+               scatterwalk_map_and_copy(req_ctx->iv, req->src, offset, ivsize, 0);
+       }
        if (req->src != req->dst) {
                struct buffer_desc dst_hook;
                crypt->mode |= NPE_OP_NOT_IN_PLACE;