static int chainiv_givencrypt(struct skcipher_givcrypt_request *req) { struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req); struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv); struct ablkcipher_request *subreq = skcipher_givcrypt_reqctx(req); unsigned int ivsize; int err; ablkcipher_request_set_tfm(subreq, skcipher_geniv_cipher(geniv)); ablkcipher_request_set_callback(subreq, req->creq.base.flags & ~CRYPTO_TFM_REQ_MAY_SLEEP, req->creq.base.complete, req->creq.base.data); ablkcipher_request_set_crypt(subreq, req->creq.src, req->creq.dst, req->creq.nbytes, req->creq.info); spin_lock_bh(&ctx->lock); ivsize = crypto_ablkcipher_ivsize(geniv); memcpy(req->giv, ctx->iv, ivsize); memcpy(subreq->info, ctx->iv, ivsize); err = crypto_ablkcipher_encrypt(subreq); if (err) goto unlock; memcpy(ctx->iv, subreq->info, ivsize); unlock: spin_unlock_bh(&ctx->lock); return err; }
static void async_chainiv_do_postponed(struct work_struct *work) { struct async_chainiv_ctx *ctx = container_of(work, struct async_chainiv_ctx, postponed); struct skcipher_givcrypt_request *req; struct ablkcipher_request *subreq; int err; /* Only handle one request at a time to avoid hogging keventd. */ spin_lock_bh(&ctx->lock); req = skcipher_dequeue_givcrypt(&ctx->queue); spin_unlock_bh(&ctx->lock); if (!req) { async_chainiv_schedule_work(ctx); return; } subreq = skcipher_givcrypt_reqctx(req); subreq->base.flags |= CRYPTO_TFM_REQ_MAY_SLEEP; err = async_chainiv_givencrypt_tail(req); local_bh_disable(); skcipher_givcrypt_complete(req, err); local_bh_enable(); }
static int async_chainiv_givencrypt(struct skcipher_givcrypt_request *req) { struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req); struct async_chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv); struct ablkcipher_request *subreq = skcipher_givcrypt_reqctx(req); ablkcipher_request_set_tfm(subreq, skcipher_geniv_cipher(geniv)); ablkcipher_request_set_callback(subreq, req->creq.base.flags, req->creq.base.complete, req->creq.base.data); ablkcipher_request_set_crypt(subreq, req->creq.src, req->creq.dst, req->creq.nbytes, req->creq.info); if (test_and_set_bit(CHAINIV_STATE_INUSE, &ctx->state)) goto postpone; if (ctx->queue.qlen) { clear_bit(CHAINIV_STATE_INUSE, &ctx->state); goto postpone; } return async_chainiv_givencrypt_tail(req); postpone: return async_chainiv_postpone_request(req); }
static void eseqiv_complete2(struct skcipher_givcrypt_request *req) { struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req); struct eseqiv_request_ctx *reqctx = skcipher_givcrypt_reqctx(req); memcpy(req->giv, PTR_ALIGN((u8 *)reqctx->tail, crypto_ablkcipher_alignmask(geniv) + 1), crypto_ablkcipher_ivsize(geniv)); }
static void seqiv_complete2(struct skcipher_givcrypt_request *req, int err) { struct ablkcipher_request *subreq = skcipher_givcrypt_reqctx(req); struct crypto_ablkcipher *geniv; if (err == -EINPROGRESS) return; if (err) goto out; geniv = skcipher_givcrypt_reqtfm(req); memcpy(req->creq.info, subreq->info, crypto_ablkcipher_ivsize(geniv)); out: kfree(subreq->info); }
static int seqiv_givencrypt(struct skcipher_givcrypt_request *req) { struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req); struct seqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv); struct ablkcipher_request *subreq = skcipher_givcrypt_reqctx(req); crypto_completion_t compl; void *data; u8 *info; unsigned int ivsize; int err; ablkcipher_request_set_tfm(subreq, skcipher_geniv_cipher(geniv)); compl = req->creq.base.complete; data = req->creq.base.data; info = req->creq.info; ivsize = crypto_ablkcipher_ivsize(geniv); if (unlikely(!IS_ALIGNED((unsigned long)info, crypto_ablkcipher_alignmask(geniv) + 1))) { info = kmalloc(ivsize, req->creq.base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL: GFP_ATOMIC); if (!info) return -ENOMEM; compl = seqiv_complete; data = req; } ablkcipher_request_set_callback(subreq, req->creq.base.flags, compl, data); ablkcipher_request_set_crypt(subreq, req->creq.src, req->creq.dst, req->creq.nbytes, info); seqiv_geniv(ctx, info, req->seq, ivsize); memcpy(req->giv, info, ivsize); err = crypto_ablkcipher_encrypt(subreq); if (unlikely(info != req->creq.info)) seqiv_complete2(req, err); return err; }
static int async_chainiv_givencrypt_tail(struct skcipher_givcrypt_request *req) { struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req); struct async_chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv); struct ablkcipher_request *subreq = skcipher_givcrypt_reqctx(req); unsigned int ivsize = crypto_ablkcipher_ivsize(geniv); memcpy(req->giv, ctx->iv, ivsize); memcpy(subreq->info, ctx->iv, ivsize); ctx->err = crypto_ablkcipher_encrypt(subreq); if (ctx->err) goto out; memcpy(ctx->iv, subreq->info, ivsize); out: return async_chainiv_schedule_work(ctx); }
static int eseqiv_givencrypt(struct skcipher_givcrypt_request *req) { struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req); struct eseqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv); struct eseqiv_request_ctx *reqctx = skcipher_givcrypt_reqctx(req); struct ablkcipher_request *subreq; crypto_completion_t complete; void *data; struct scatterlist *osrc, *odst; struct scatterlist *dst; struct page *srcp; struct page *dstp; u8 *giv; u8 *vsrc; u8 *vdst; __be64 seq; unsigned int ivsize; unsigned int len; int err; subreq = (void *)(reqctx->tail + ctx->reqoff); ablkcipher_request_set_tfm(subreq, skcipher_geniv_cipher(geniv)); giv = req->giv; complete = req->creq.base.complete; data = req->creq.base.data; osrc = req->creq.src; odst = req->creq.dst; srcp = sg_page(osrc); dstp = sg_page(odst); vsrc = PageHighMem(srcp) ? NULL : page_address(srcp) + osrc->offset; vdst = PageHighMem(dstp) ? NULL : page_address(dstp) + odst->offset; ivsize = crypto_ablkcipher_ivsize(geniv); if (vsrc != giv + ivsize && vdst != giv + ivsize) { giv = PTR_ALIGN((u8 *)reqctx->tail, crypto_ablkcipher_alignmask(geniv) + 1); complete = eseqiv_complete; data = req; } ablkcipher_request_set_callback(subreq, req->creq.base.flags, complete, data); sg_init_table(reqctx->src, 2); sg_set_buf(reqctx->src, giv, ivsize); scatterwalk_crypto_chain(reqctx->src, osrc, vsrc == giv + ivsize, 2); dst = reqctx->src; if (osrc != odst) { sg_init_table(reqctx->dst, 2); sg_set_buf(reqctx->dst, giv, ivsize); scatterwalk_crypto_chain(reqctx->dst, odst, vdst == giv + ivsize, 2); dst = reqctx->dst; } ablkcipher_request_set_crypt(subreq, reqctx->src, dst, req->creq.nbytes + ivsize, req->creq.info); memcpy(req->creq.info, ctx->salt, ivsize); len = ivsize; if (ivsize > sizeof(u64)) { memset(req->giv, 0, ivsize - sizeof(u64)); len = sizeof(u64); } seq = cpu_to_be64(req->seq); memcpy(req->giv + ivsize - len, &seq, len); err = crypto_ablkcipher_encrypt(subreq); if (err) goto out; if (giv != req->giv) eseqiv_complete2(req); out: return err; }