Skip to content

Commit

Permalink
[CRYPTO] authenc: Add givencrypt operation
Browse files Browse the repository at this point in the history
This patch implements the givencrypt function for authenc.  It simply
calls the givencrypt operation on the underlying cipher instead of encrypt.

Signed-off-by: Herbert Xu <[email protected]>
  • Loading branch information
herbertx committed Jan 10, 2008
1 parent 743edf5 commit e56dd56
Showing 1 changed file with 114 additions and 22 deletions.
136 changes: 114 additions & 22 deletions crypto/authenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*
*/

#include <crypto/aead.h>
#include <crypto/internal/skcipher.h>
#include <crypto/authenc.h>
#include <crypto/scatterwalk.h>
Expand Down Expand Up @@ -87,6 +88,20 @@ static int crypto_authenc_setkey(struct crypto_aead *authenc, const u8 *key,
goto out;
}

static void authenc_chain(struct scatterlist *head, struct scatterlist *sg,
int chain)
{
if (chain) {
head->length += sg->length;
sg = scatterwalk_sg_next(sg);
}

if (sg)
scatterwalk_sg_chain(head, 2, sg);
else
sg_mark_end(head);
}

static u8 *crypto_authenc_hash(struct aead_request *req, unsigned int flags,
struct scatterlist *cipher,
unsigned int cryptlen)
Expand Down Expand Up @@ -127,27 +142,48 @@ static u8 *crypto_authenc_hash(struct aead_request *req, unsigned int flags,
return hash;
}

static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
static int crypto_authenc_genicv(struct aead_request *req, u8 *iv,
unsigned int flags)
{
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct scatterlist *dst = req->dst;
unsigned int cryptlen = req->cryptlen;
struct scatterlist cipher[2];
struct page *dstp;
unsigned int ivsize = crypto_aead_ivsize(authenc);
unsigned int cryptlen;
u8 *vdst;
u8 *hash;

hash = crypto_authenc_hash(req, flags, dst, cryptlen);
dstp = sg_page(dst);
vdst = PageHighMem(dstp) ? NULL : page_address(dstp) + dst->offset;

sg_init_table(cipher, 2);
sg_set_buf(cipher, iv, ivsize);
authenc_chain(cipher, dst, vdst == iv + ivsize);

cryptlen = req->cryptlen + ivsize;
hash = crypto_authenc_hash(req, flags, cipher, cryptlen);
if (IS_ERR(hash))
return PTR_ERR(hash);

scatterwalk_map_and_copy(hash, dst, cryptlen,
scatterwalk_map_and_copy(hash, cipher, cryptlen,
crypto_aead_authsize(authenc), 1);
return 0;
}

static void crypto_authenc_encrypt_done(struct crypto_async_request *req,
int err)
{
if (!err)
err = crypto_authenc_genicv(req->data, 0);
if (!err) {
struct aead_request *areq = req->data;
struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
struct ablkcipher_request *abreq = aead_request_ctx(areq);
u8 *iv = (u8 *)(abreq + 1) +
crypto_ablkcipher_reqsize(ctx->enc);

err = crypto_authenc_genicv(areq, iv, 0);
}

aead_request_complete(req->data, err);
}
Expand All @@ -157,45 +193,99 @@ static int crypto_authenc_encrypt(struct aead_request *req)
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
struct ablkcipher_request *abreq = aead_request_ctx(req);
struct crypto_ablkcipher *enc = ctx->enc;
struct scatterlist *dst = req->dst;
unsigned int cryptlen = req->cryptlen;
u8 *iv = (u8 *)(abreq + 1) + crypto_ablkcipher_reqsize(enc);
int err;

ablkcipher_request_set_tfm(abreq, ctx->enc);
ablkcipher_request_set_tfm(abreq, enc);
ablkcipher_request_set_callback(abreq, aead_request_flags(req),
crypto_authenc_encrypt_done, req);
ablkcipher_request_set_crypt(abreq, req->src, req->dst, req->cryptlen,
req->iv);
ablkcipher_request_set_crypt(abreq, req->src, dst, cryptlen, req->iv);

memcpy(iv, req->iv, crypto_aead_ivsize(authenc));

err = crypto_ablkcipher_encrypt(abreq);
if (err)
return err;

return crypto_authenc_genicv(req, CRYPTO_TFM_REQ_MAY_SLEEP);
return crypto_authenc_genicv(req, iv, CRYPTO_TFM_REQ_MAY_SLEEP);
}

static void crypto_authenc_givencrypt_done(struct crypto_async_request *req,
int err)
{
if (!err) {
struct aead_givcrypt_request *greq = req->data;

err = crypto_authenc_genicv(&greq->areq, greq->giv, 0);
}

aead_request_complete(req->data, err);
}

static int crypto_authenc_givencrypt(struct aead_givcrypt_request *req)
{
struct crypto_aead *authenc = aead_givcrypt_reqtfm(req);
struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
struct aead_request *areq = &req->areq;
struct skcipher_givcrypt_request *greq = aead_request_ctx(areq);
u8 *iv = req->giv;
int err;

skcipher_givcrypt_set_tfm(greq, ctx->enc);
skcipher_givcrypt_set_callback(greq, aead_request_flags(areq),
crypto_authenc_givencrypt_done, areq);
skcipher_givcrypt_set_crypt(greq, areq->src, areq->dst, areq->cryptlen,
areq->iv);
skcipher_givcrypt_set_giv(greq, iv, req->seq);

err = crypto_skcipher_givencrypt(greq);
if (err)
return err;

return crypto_authenc_genicv(areq, iv, CRYPTO_TFM_REQ_MAY_SLEEP);
}

static int crypto_authenc_verify(struct aead_request *req,
struct scatterlist *cipher,
unsigned int cryptlen)
{
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
u8 *ohash;
u8 *ihash;
struct scatterlist *src = req->src;
unsigned int authsize;

ohash = crypto_authenc_hash(req, CRYPTO_TFM_REQ_MAY_SLEEP, src,
ohash = crypto_authenc_hash(req, CRYPTO_TFM_REQ_MAY_SLEEP, cipher,
cryptlen);
if (IS_ERR(ohash))
return PTR_ERR(ohash);

authsize = crypto_aead_authsize(authenc);
ihash = ohash + authsize;
scatterwalk_map_and_copy(ihash, src, cryptlen, authsize, 0);
scatterwalk_map_and_copy(ihash, cipher, cryptlen, authsize, 0);
return memcmp(ihash, ohash, authsize) ? -EBADMSG: 0;
}

static void crypto_authenc_decrypt_done(struct crypto_async_request *req,
int err)
static int crypto_authenc_iverify(struct aead_request *req, u8 *iv,
unsigned int cryptlen)
{
aead_request_complete(req->data, err);
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct scatterlist *src = req->src;
struct scatterlist cipher[2];
struct page *srcp;
unsigned int ivsize = crypto_aead_ivsize(authenc);
u8 *vsrc;

srcp = sg_page(src);
vsrc = PageHighMem(srcp) ? NULL : page_address(srcp) + src->offset;

sg_init_table(cipher, 2);
sg_set_buf(cipher, iv, ivsize);
authenc_chain(cipher, src, vsrc == iv + ivsize);

return crypto_authenc_verify(req, cipher, cryptlen + ivsize);
}

static int crypto_authenc_decrypt(struct aead_request *req)
Expand All @@ -205,21 +295,21 @@ static int crypto_authenc_decrypt(struct aead_request *req)
struct ablkcipher_request *abreq = aead_request_ctx(req);
unsigned int cryptlen = req->cryptlen;
unsigned int authsize = crypto_aead_authsize(authenc);
u8 *iv = req->iv;
int err;

if (cryptlen < authsize)
return -EINVAL;
cryptlen -= authsize;

err = crypto_authenc_verify(req, cryptlen);
err = crypto_authenc_iverify(req, iv, cryptlen);
if (err)
return err;

ablkcipher_request_set_tfm(abreq, ctx->enc);
ablkcipher_request_set_callback(abreq, aead_request_flags(req),
crypto_authenc_decrypt_done, req);
ablkcipher_request_set_crypt(abreq, req->src, req->dst, cryptlen,
req->iv);
req->base.complete, req->base.data);
ablkcipher_request_set_crypt(abreq, req->src, req->dst, cryptlen, iv);

return crypto_ablkcipher_decrypt(abreq);
}
Expand Down Expand Up @@ -248,8 +338,9 @@ static int crypto_authenc_init_tfm(struct crypto_tfm *tfm)
(crypto_hash_alignmask(auth) &
~(crypto_tfm_ctx_alignment() - 1)) +
crypto_hash_digestsize(auth) * 2,
sizeof(struct ablkcipher_request) +
crypto_ablkcipher_reqsize(enc));
sizeof(struct skcipher_givcrypt_request) +
crypto_ablkcipher_reqsize(enc) +
crypto_ablkcipher_ivsize(enc));

spin_lock_init(&ctx->auth_lock);

Expand Down Expand Up @@ -347,6 +438,7 @@ static struct crypto_instance *crypto_authenc_alloc(struct rtattr **tb)
inst->alg.cra_aead.setkey = crypto_authenc_setkey;
inst->alg.cra_aead.encrypt = crypto_authenc_encrypt;
inst->alg.cra_aead.decrypt = crypto_authenc_decrypt;
inst->alg.cra_aead.givencrypt = crypto_authenc_givencrypt;

out:
crypto_mod_put(auth);
Expand Down

0 comments on commit e56dd56

Please sign in to comment.