diff options
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/seqiv.c | 191 |
1 files changed, 175 insertions, 16 deletions
diff --git a/crypto/seqiv.c b/crypto/seqiv.c index 9c2d80d77a0..b903aab3157 100644 --- a/crypto/seqiv.c +++ b/crypto/seqiv.c @@ -13,6 +13,7 @@ * */ +#include <crypto/internal/aead.h> #include <crypto/internal/skcipher.h> #include <linux/err.h> #include <linux/init.h> @@ -53,6 +54,46 @@ static void seqiv_complete(struct crypto_async_request *base, int err) skcipher_givcrypt_complete(req, err); } +static void seqiv_aead_complete2(struct aead_givcrypt_request *req, int err) +{ + struct aead_request *subreq = aead_givcrypt_reqctx(req); + struct crypto_aead *geniv; + + if (err == -EINPROGRESS) + return; + + if (err) + goto out; + + geniv = aead_givcrypt_reqtfm(req); + memcpy(req->areq.iv, subreq->iv, crypto_aead_ivsize(geniv)); + +out: + kfree(subreq->iv); +} + +static void seqiv_aead_complete(struct crypto_async_request *base, int err) +{ + struct aead_givcrypt_request *req = base->data; + + seqiv_aead_complete2(req, err); + aead_givcrypt_complete(req, err); +} + +static void seqiv_geniv(struct seqiv_ctx *ctx, u8 *info, u64 seq, + unsigned int ivsize) +{ + unsigned int len = ivsize; + + if (ivsize > sizeof(u64)) { + memset(info, 0, ivsize - sizeof(u64)); + len = sizeof(u64); + } + seq = cpu_to_be64(seq); + memcpy(info + ivsize - len, &seq, len); + crypto_xor(info, ctx->salt, ivsize); +} + static int seqiv_givencrypt(struct skcipher_givcrypt_request *req) { struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req); @@ -61,9 +102,7 @@ static int seqiv_givencrypt(struct skcipher_givcrypt_request *req) crypto_completion_t complete; void *data; u8 *info; - __be64 seq; unsigned int ivsize; - unsigned int len; int err; ablkcipher_request_set_tfm(subreq, skcipher_geniv_cipher(geniv)); @@ -91,15 +130,7 @@ static int seqiv_givencrypt(struct skcipher_givcrypt_request *req) ablkcipher_request_set_crypt(subreq, req->creq.src, req->creq.dst, req->creq.nbytes, info); - len = ivsize; - if (ivsize > sizeof(u64)) { - memset(info, 0, ivsize - sizeof(u64)); - len = sizeof(u64); - } - seq = cpu_to_be64(req->seq); - memcpy(info + ivsize - len, &seq, len); - crypto_xor(info, ctx->salt, ivsize); - + seqiv_geniv(ctx, info, req->seq, ivsize); memcpy(req->giv, info, ivsize); err = crypto_ablkcipher_encrypt(subreq); @@ -108,6 +139,52 @@ static int seqiv_givencrypt(struct skcipher_givcrypt_request *req) return err; } +static int seqiv_aead_givencrypt(struct aead_givcrypt_request *req) +{ + struct crypto_aead *geniv = aead_givcrypt_reqtfm(req); + struct seqiv_ctx *ctx = crypto_aead_ctx(geniv); + struct aead_request *areq = &req->areq; + struct aead_request *subreq = aead_givcrypt_reqctx(req); + crypto_completion_t complete; + void *data; + u8 *info; + unsigned int ivsize; + int err; + + aead_request_set_tfm(subreq, aead_geniv_base(geniv)); + + complete = areq->base.complete; + data = areq->base.data; + info = areq->iv; + + ivsize = crypto_aead_ivsize(geniv); + + if (unlikely(!IS_ALIGNED((unsigned long)info, + crypto_aead_alignmask(geniv) + 1))) { + info = kmalloc(ivsize, areq->base.flags & + CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL: + GFP_ATOMIC); + if (!info) + return -ENOMEM; + + complete = seqiv_aead_complete; + data = req; + } + + aead_request_set_callback(subreq, areq->base.flags, complete, data); + aead_request_set_crypt(subreq, areq->src, areq->dst, areq->cryptlen, + info); + aead_request_set_assoc(subreq, areq->assoc, areq->assoclen); + + seqiv_geniv(ctx, info, req->seq, ivsize); + memcpy(req->giv, info, ivsize); + + err = crypto_aead_encrypt(subreq); + if (unlikely(info != areq->iv)) + seqiv_aead_complete2(req, err); + return err; +} + static int seqiv_givencrypt_first(struct skcipher_givcrypt_request *req) { struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req); @@ -126,6 +203,24 @@ unlock: return seqiv_givencrypt(req); } +static int seqiv_aead_givencrypt_first(struct aead_givcrypt_request *req) +{ + struct crypto_aead *geniv = aead_givcrypt_reqtfm(req); + struct seqiv_ctx *ctx = crypto_aead_ctx(geniv); + + spin_lock_bh(&ctx->lock); + if (crypto_aead_crt(geniv)->givencrypt != seqiv_aead_givencrypt_first) + goto unlock; + + crypto_aead_crt(geniv)->givencrypt = seqiv_aead_givencrypt; + get_random_bytes(ctx->salt, crypto_aead_ivsize(geniv)); + +unlock: + spin_unlock_bh(&ctx->lock); + + return seqiv_aead_givencrypt(req); +} + static int seqiv_init(struct crypto_tfm *tfm) { struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm); @@ -138,13 +233,26 @@ static int seqiv_init(struct crypto_tfm *tfm) return skcipher_geniv_init(tfm); } +static int seqiv_aead_init(struct crypto_tfm *tfm) +{ + struct crypto_aead *geniv = __crypto_aead_cast(tfm); + struct seqiv_ctx *ctx = crypto_aead_ctx(geniv); + + spin_lock_init(&ctx->lock); + + tfm->crt_aead.reqsize = sizeof(struct aead_request); + + return aead_geniv_init(tfm); +} + static struct crypto_template seqiv_tmpl; -static struct crypto_instance *seqiv_alloc(struct rtattr **tb) +static struct crypto_instance *seqiv_ablkcipher_alloc(struct rtattr **tb) { struct crypto_instance *inst; inst = skcipher_geniv_alloc(&seqiv_tmpl, tb, 0, 0); + if (IS_ERR(inst)) goto out; @@ -153,19 +261,70 @@ static struct crypto_instance *seqiv_alloc(struct rtattr **tb) inst->alg.cra_init = seqiv_init; inst->alg.cra_exit = skcipher_geniv_exit; - inst->alg.cra_alignmask |= __alignof__(u32) - 1; - - inst->alg.cra_ctxsize = sizeof(struct seqiv_ctx); inst->alg.cra_ctxsize += inst->alg.cra_ablkcipher.ivsize; out: return inst; } +static struct crypto_instance *seqiv_aead_alloc(struct rtattr **tb) +{ + struct crypto_instance *inst; + + inst = aead_geniv_alloc(&seqiv_tmpl, tb, 0, 0); + + if (IS_ERR(inst)) + goto out; + + inst->alg.cra_aead.givencrypt = seqiv_aead_givencrypt_first; + + inst->alg.cra_init = seqiv_aead_init; + inst->alg.cra_exit = aead_geniv_exit; + + inst->alg.cra_ctxsize = inst->alg.cra_aead.ivsize; + +out: + return inst; +} + +static struct crypto_instance *seqiv_alloc(struct rtattr **tb) +{ + struct crypto_attr_type *algt; + struct crypto_instance *inst; + int err; + + algt = crypto_get_attr_type(tb); + err = PTR_ERR(algt); + if (IS_ERR(algt)) + return ERR_PTR(err); + + if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & CRYPTO_ALG_TYPE_MASK) + inst = seqiv_ablkcipher_alloc(tb); + else + inst = seqiv_aead_alloc(tb); + + if (IS_ERR(inst)) + goto out; + + inst->alg.cra_alignmask |= __alignof__(u32) - 1; + inst->alg.cra_ctxsize += sizeof(struct seqiv_ctx); + +out: + return inst; +} + +static void seqiv_free(struct crypto_instance *inst) +{ + if ((inst->alg.cra_flags ^ CRYPTO_ALG_TYPE_AEAD) & CRYPTO_ALG_TYPE_MASK) + skcipher_geniv_free(inst); + else + aead_geniv_free(inst); +} + static struct crypto_template seqiv_tmpl = { .name = "seqiv", .alloc = seqiv_alloc, - .free = skcipher_geniv_free, + .free = seqiv_free, .module = THIS_MODULE, }; |