linux/crypto/authenc.c
<<
>>
Prefs
   1/*
   2 * Authenc: Simple AEAD wrapper for IPsec
   3 *
   4 * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License as published by the Free
   8 * Software Foundation; either version 2 of the License, or (at your option)
   9 * any later version.
  10 *
  11 */
  12
  13#include <crypto/algapi.h>
  14#include <linux/err.h>
  15#include <linux/init.h>
  16#include <linux/kernel.h>
  17#include <linux/module.h>
  18#include <linux/slab.h>
  19#include <linux/spinlock.h>
  20
  21#include "scatterwalk.h"
  22
  23struct authenc_instance_ctx {
  24        struct crypto_spawn auth;
  25        struct crypto_spawn enc;
  26
  27        unsigned int authsize;
  28        unsigned int enckeylen;
  29};
  30
  31struct crypto_authenc_ctx {
  32        spinlock_t auth_lock;
  33        struct crypto_hash *auth;
  34        struct crypto_ablkcipher *enc;
  35};
  36
  37static int crypto_authenc_setkey(struct crypto_aead *authenc, const u8 *key,
  38                                 unsigned int keylen)
  39{
  40        struct authenc_instance_ctx *ictx =
  41                crypto_instance_ctx(crypto_aead_alg_instance(authenc));
  42        unsigned int enckeylen = ictx->enckeylen;
  43        unsigned int authkeylen;
  44        struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
  45        struct crypto_hash *auth = ctx->auth;
  46        struct crypto_ablkcipher *enc = ctx->enc;
  47        int err = -EINVAL;
  48
  49        if (keylen < enckeylen) {
  50                crypto_aead_set_flags(authenc, CRYPTO_TFM_RES_BAD_KEY_LEN);
  51                goto out;
  52        }
  53        authkeylen = keylen - enckeylen;
  54
  55        crypto_hash_clear_flags(auth, CRYPTO_TFM_REQ_MASK);
  56        crypto_hash_set_flags(auth, crypto_aead_get_flags(authenc) &
  57                                    CRYPTO_TFM_REQ_MASK);
  58        err = crypto_hash_setkey(auth, key, authkeylen);
  59        crypto_aead_set_flags(authenc, crypto_hash_get_flags(auth) &
  60                                       CRYPTO_TFM_RES_MASK);
  61
  62        if (err)
  63                goto out;
  64
  65        crypto_ablkcipher_clear_flags(enc, CRYPTO_TFM_REQ_MASK);
  66        crypto_ablkcipher_set_flags(enc, crypto_aead_get_flags(authenc) &
  67                                         CRYPTO_TFM_REQ_MASK);
  68        err = crypto_ablkcipher_setkey(enc, key + authkeylen, enckeylen);
  69        crypto_aead_set_flags(authenc, crypto_ablkcipher_get_flags(enc) &
  70                                       CRYPTO_TFM_RES_MASK);
  71
  72out:
  73        return err;
  74}
  75
  76static int crypto_authenc_hash(struct aead_request *req)
  77{
  78        struct crypto_aead *authenc = crypto_aead_reqtfm(req);
  79        struct authenc_instance_ctx *ictx =
  80                crypto_instance_ctx(crypto_aead_alg_instance(authenc));
  81        struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
  82        struct crypto_hash *auth = ctx->auth;
  83        struct hash_desc desc = {
  84                .tfm = auth,
  85        };
  86        u8 *hash = aead_request_ctx(req);
  87        struct scatterlist *dst = req->dst;
  88        unsigned int cryptlen = req->cryptlen;
  89        int err;
  90
  91        hash = (u8 *)ALIGN((unsigned long)hash + crypto_hash_alignmask(auth), 
  92                           crypto_hash_alignmask(auth) + 1);
  93
  94        spin_lock_bh(&ctx->auth_lock);
  95        err = crypto_hash_init(&desc);
  96        if (err)
  97                goto auth_unlock;
  98
  99        err = crypto_hash_update(&desc, req->assoc, req->assoclen);
 100        if (err)
 101                goto auth_unlock;
 102
 103        err = crypto_hash_update(&desc, dst, cryptlen);
 104        if (err)
 105                goto auth_unlock;
 106
 107        err = crypto_hash_final(&desc, hash);
 108auth_unlock:
 109        spin_unlock_bh(&ctx->auth_lock);
 110
 111        if (err)
 112                return err;
 113
 114        scatterwalk_map_and_copy(hash, dst, cryptlen, ictx->authsize, 1);
 115        return 0;
 116}
 117
 118static void crypto_authenc_encrypt_done(struct crypto_async_request *req,
 119                                        int err)
 120{
 121        if (!err)
 122                err = crypto_authenc_hash(req->data);
 123
 124        aead_request_complete(req->data, err);
 125}
 126
 127static int crypto_authenc_encrypt(struct aead_request *req)
 128{
 129        struct crypto_aead *authenc = crypto_aead_reqtfm(req);
 130        struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
 131        struct ablkcipher_request *abreq = aead_request_ctx(req);
 132        int err;
 133
 134        ablkcipher_request_set_tfm(abreq, ctx->enc);
 135        ablkcipher_request_set_callback(abreq, aead_request_flags(req),
 136                                        crypto_authenc_encrypt_done, req);
 137        ablkcipher_request_set_crypt(abreq, req->src, req->dst, req->cryptlen,
 138                                     req->iv);
 139
 140        err = crypto_ablkcipher_encrypt(abreq);
 141        if (err)
 142                return err;
 143
 144        return crypto_authenc_hash(req);
 145}
 146
 147static int crypto_authenc_verify(struct aead_request *req)
 148{
 149        struct crypto_aead *authenc = crypto_aead_reqtfm(req);
 150        struct authenc_instance_ctx *ictx =
 151                crypto_instance_ctx(crypto_aead_alg_instance(authenc));
 152        struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
 153        struct crypto_hash *auth = ctx->auth;
 154        struct hash_desc desc = {
 155                .tfm = auth,
 156                .flags = aead_request_flags(req),
 157        };
 158        u8 *ohash = aead_request_ctx(req);
 159        u8 *ihash;
 160        struct scatterlist *src = req->src;
 161        unsigned int cryptlen = req->cryptlen;
 162        unsigned int authsize;
 163        int err;
 164
 165        ohash = (u8 *)ALIGN((unsigned long)ohash + crypto_hash_alignmask(auth), 
 166                            crypto_hash_alignmask(auth) + 1);
 167        ihash = ohash + crypto_hash_digestsize(auth);
 168
 169        spin_lock_bh(&ctx->auth_lock);
 170        err = crypto_hash_init(&desc);
 171        if (err)
 172                goto auth_unlock;
 173
 174        err = crypto_hash_update(&desc, req->assoc, req->assoclen);
 175        if (err)
 176                goto auth_unlock;
 177
 178        err = crypto_hash_update(&desc, src, cryptlen);
 179        if (err)
 180                goto auth_unlock;
 181
 182        err = crypto_hash_final(&desc, ohash);
 183auth_unlock:
 184        spin_unlock_bh(&ctx->auth_lock);
 185
 186        if (err)
 187                return err;
 188
 189        authsize = ictx->authsize;
 190        scatterwalk_map_and_copy(ihash, src, cryptlen, authsize, 0);
 191        return memcmp(ihash, ohash, authsize) ? -EINVAL : 0;
 192}
 193
 194static void crypto_authenc_decrypt_done(struct crypto_async_request *req,
 195                                        int err)
 196{
 197        aead_request_complete(req->data, err);
 198}
 199
 200static int crypto_authenc_decrypt(struct aead_request *req)
 201{
 202        struct crypto_aead *authenc = crypto_aead_reqtfm(req);
 203        struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
 204        struct ablkcipher_request *abreq = aead_request_ctx(req);
 205        int err;
 206
 207        err = crypto_authenc_verify(req);
 208        if (err)
 209                return err;
 210
 211        ablkcipher_request_set_tfm(abreq, ctx->enc);
 212        ablkcipher_request_set_callback(abreq, aead_request_flags(req),
 213                                        crypto_authenc_decrypt_done, req);
 214        ablkcipher_request_set_crypt(abreq, req->src, req->dst, req->cryptlen,
 215                                     req->iv);
 216
 217        return crypto_ablkcipher_decrypt(abreq);
 218}
 219
 220static int crypto_authenc_init_tfm(struct crypto_tfm *tfm)
 221{
 222        struct crypto_instance *inst = (void *)tfm->__crt_alg;
 223        struct authenc_instance_ctx *ictx = crypto_instance_ctx(inst);
 224        struct crypto_authenc_ctx *ctx = crypto_tfm_ctx(tfm);
 225        struct crypto_hash *auth;
 226        struct crypto_ablkcipher *enc;
 227        unsigned int digestsize;
 228        int err;
 229
 230        auth = crypto_spawn_hash(&ictx->auth);
 231        if (IS_ERR(auth))
 232                return PTR_ERR(auth);
 233
 234        err = -EINVAL;
 235        digestsize = crypto_hash_digestsize(auth);
 236        if (ictx->authsize > digestsize)
 237                goto err_free_hash;
 238
 239        enc = crypto_spawn_ablkcipher(&ictx->enc);
 240        err = PTR_ERR(enc);
 241        if (IS_ERR(enc))
 242                goto err_free_hash;
 243
 244        ctx->auth = auth;
 245        ctx->enc = enc;
 246        tfm->crt_aead.reqsize = max_t(unsigned int,
 247                                      (crypto_hash_alignmask(auth) &
 248                                       ~(crypto_tfm_ctx_alignment() - 1)) +
 249                                      digestsize * 2,
 250                                      sizeof(struct ablkcipher_request) +
 251                                      crypto_ablkcipher_reqsize(enc));
 252
 253        spin_lock_init(&ctx->auth_lock);
 254
 255        return 0;
 256
 257err_free_hash:
 258        crypto_free_hash(auth);
 259        return err;
 260}
 261
 262static void crypto_authenc_exit_tfm(struct crypto_tfm *tfm)
 263{
 264        struct crypto_authenc_ctx *ctx = crypto_tfm_ctx(tfm);
 265
 266        crypto_free_hash(ctx->auth);
 267        crypto_free_ablkcipher(ctx->enc);
 268}
 269
 270static struct crypto_instance *crypto_authenc_alloc(struct rtattr **tb)
 271{
 272        struct crypto_instance *inst;
 273        struct crypto_alg *auth;
 274        struct crypto_alg *enc;
 275        struct authenc_instance_ctx *ctx;
 276        unsigned int authsize;
 277        unsigned int enckeylen;
 278        int err;
 279
 280        err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_AEAD);
 281        if (err)
 282                return ERR_PTR(err);
 283
 284        auth = crypto_attr_alg(tb[1], CRYPTO_ALG_TYPE_HASH,
 285                               CRYPTO_ALG_TYPE_HASH_MASK);
 286        if (IS_ERR(auth))
 287                return ERR_PTR(PTR_ERR(auth));
 288
 289        err = crypto_attr_u32(tb[2], &authsize);
 290        inst = ERR_PTR(err);
 291        if (err)
 292                goto out_put_auth;
 293
 294        enc = crypto_attr_alg(tb[3], CRYPTO_ALG_TYPE_BLKCIPHER,
 295                              CRYPTO_ALG_TYPE_MASK);
 296        inst = ERR_PTR(PTR_ERR(enc));
 297        if (IS_ERR(enc))
 298                goto out_put_auth;
 299
 300        err = crypto_attr_u32(tb[4], &enckeylen);
 301        if (err)
 302                goto out_put_enc;
 303
 304        inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
 305        err = -ENOMEM;
 306        if (!inst)
 307                goto out_put_enc;
 308
 309        err = -ENAMETOOLONG;
 310        if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME,
 311                     "authenc(%s,%u,%s,%u)", auth->cra_name, authsize,
 312                     enc->cra_name, enckeylen) >= CRYPTO_MAX_ALG_NAME)
 313                goto err_free_inst;
 314
 315        if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
 316                     "authenc(%s,%u,%s,%u)", auth->cra_driver_name,
 317                     authsize, enc->cra_driver_name, enckeylen) >=
 318            CRYPTO_MAX_ALG_NAME)
 319                goto err_free_inst;
 320
 321        ctx = crypto_instance_ctx(inst);
 322        ctx->authsize = authsize;
 323        ctx->enckeylen = enckeylen;
 324
 325        err = crypto_init_spawn(&ctx->auth, auth, inst, CRYPTO_ALG_TYPE_MASK);
 326        if (err)
 327                goto err_free_inst;
 328
 329        err = crypto_init_spawn(&ctx->enc, enc, inst, CRYPTO_ALG_TYPE_MASK);
 330        if (err)
 331                goto err_drop_auth;
 332
 333        inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC;
 334        inst->alg.cra_priority = enc->cra_priority * 10 + auth->cra_priority;
 335        inst->alg.cra_blocksize = enc->cra_blocksize;
 336        inst->alg.cra_alignmask = max(auth->cra_alignmask, enc->cra_alignmask);
 337        inst->alg.cra_type = &crypto_aead_type;
 338
 339        inst->alg.cra_aead.ivsize = enc->cra_blkcipher.ivsize;
 340        inst->alg.cra_aead.authsize = authsize;
 341
 342        inst->alg.cra_ctxsize = sizeof(struct crypto_authenc_ctx);
 343
 344        inst->alg.cra_init = crypto_authenc_init_tfm;
 345        inst->alg.cra_exit = crypto_authenc_exit_tfm;
 346
 347        inst->alg.cra_aead.setkey = crypto_authenc_setkey;
 348        inst->alg.cra_aead.encrypt = crypto_authenc_encrypt;
 349        inst->alg.cra_aead.decrypt = crypto_authenc_decrypt;
 350
 351out:
 352        crypto_mod_put(enc);
 353out_put_auth:
 354        crypto_mod_put(auth);
 355        return inst;
 356
 357err_drop_auth:
 358        crypto_drop_spawn(&ctx->auth);
 359err_free_inst:
 360        kfree(inst);
 361out_put_enc:
 362        inst = ERR_PTR(err);
 363        goto out;
 364}
 365
 366static void crypto_authenc_free(struct crypto_instance *inst)
 367{
 368        struct authenc_instance_ctx *ctx = crypto_instance_ctx(inst);
 369
 370        crypto_drop_spawn(&ctx->enc);
 371        crypto_drop_spawn(&ctx->auth);
 372        kfree(inst);
 373}
 374
 375static struct crypto_template crypto_authenc_tmpl = {
 376        .name = "authenc",
 377        .alloc = crypto_authenc_alloc,
 378        .free = crypto_authenc_free,
 379        .module = THIS_MODULE,
 380};
 381
 382static int __init crypto_authenc_module_init(void)
 383{
 384        return crypto_register_template(&crypto_authenc_tmpl);
 385}
 386
 387static void __exit crypto_authenc_module_exit(void)
 388{
 389        crypto_unregister_template(&crypto_authenc_tmpl);
 390}
 391
 392module_init(crypto_authenc_module_init);
 393module_exit(crypto_authenc_module_exit);
 394
 395MODULE_LICENSE("GPL");
 396MODULE_DESCRIPTION("Simple AEAD wrapper for IPsec");
 397