linux/crypto/api.c
<<
>>
Prefs
   1/*
   2 * Scatterlist Cryptographic API.
   3 *
   4 * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
   5 * Copyright (c) 2002 David S. Miller (davem@redhat.com)
   6 * Copyright (c) 2005 Herbert Xu <herbert@gondor.apana.org.au>
   7 *
   8 * Portions derived from Cryptoapi, by Alexander Kjeldaas <astor@fast.no>
   9 * and Nettle, by Niels Möller.
  10 *
  11 * This program is free software; you can redistribute it and/or modify it
  12 * under the terms of the GNU General Public License as published by the Free
  13 * Software Foundation; either version 2 of the License, or (at your option) 
  14 * any later version.
  15 *
  16 */
  17
  18#include <linux/compiler.h>
  19#include <linux/init.h>
  20#include <linux/crypto.h>
  21#include <linux/errno.h>
  22#include <linux/kernel.h>
  23#include <linux/kmod.h>
  24#include <linux/rwsem.h>
  25#include <linux/slab.h>
  26#include <linux/string.h>
  27#include "internal.h"
  28
  29LIST_HEAD(crypto_alg_list);
  30DECLARE_RWSEM(crypto_alg_sem);
  31
  32static inline int crypto_alg_get(struct crypto_alg *alg)
  33{
  34        return try_module_get(alg->cra_module);
  35}
  36
  37static inline void crypto_alg_put(struct crypto_alg *alg)
  38{
  39        module_put(alg->cra_module);
  40}
  41
  42static struct crypto_alg *crypto_alg_lookup(const char *name)
  43{
  44        struct crypto_alg *q, *alg = NULL;
  45        int best = -1;
  46
  47        if (!name)
  48                return NULL;
  49        
  50        down_read(&crypto_alg_sem);
  51        
  52        list_for_each_entry(q, &crypto_alg_list, cra_list) {
  53                int exact, fuzzy;
  54
  55                exact = !strcmp(q->cra_driver_name, name);
  56                fuzzy = !strcmp(q->cra_name, name);
  57                if (!exact && !(fuzzy && q->cra_priority > best))
  58                        continue;
  59
  60                if (unlikely(!crypto_alg_get(q)))
  61                        continue;
  62
  63                best = q->cra_priority;
  64                if (alg)
  65                        crypto_alg_put(alg);
  66                alg = q;
  67
  68                if (exact)
  69                        break;
  70        }
  71        
  72        up_read(&crypto_alg_sem);
  73        return alg;
  74}
  75
  76/* A far more intelligent version of this is planned.  For now, just
  77 * try an exact match on the name of the algorithm. */
  78static inline struct crypto_alg *crypto_alg_mod_lookup(const char *name)
  79{
  80        return try_then_request_module(crypto_alg_lookup(name), name);
  81}
  82
  83static int crypto_init_flags(struct crypto_tfm *tfm, u32 flags)
  84{
  85        tfm->crt_flags = flags & CRYPTO_TFM_REQ_MASK;
  86        flags &= ~CRYPTO_TFM_REQ_MASK;
  87        
  88        switch (crypto_tfm_alg_type(tfm)) {
  89        case CRYPTO_ALG_TYPE_CIPHER:
  90                return crypto_init_cipher_flags(tfm, flags);
  91                
  92        case CRYPTO_ALG_TYPE_DIGEST:
  93                return crypto_init_digest_flags(tfm, flags);
  94                
  95        case CRYPTO_ALG_TYPE_COMPRESS:
  96                return crypto_init_compress_flags(tfm, flags);
  97        
  98        default:
  99                break;
 100        }
 101        
 102        BUG();
 103        return -EINVAL;
 104}
 105
 106static int crypto_init_ops(struct crypto_tfm *tfm)
 107{
 108        switch (crypto_tfm_alg_type(tfm)) {
 109        case CRYPTO_ALG_TYPE_CIPHER:
 110                return crypto_init_cipher_ops(tfm);
 111                
 112        case CRYPTO_ALG_TYPE_DIGEST:
 113                return crypto_init_digest_ops(tfm);
 114                
 115        case CRYPTO_ALG_TYPE_COMPRESS:
 116                return crypto_init_compress_ops(tfm);
 117        
 118        default:
 119                break;
 120        }
 121        
 122        BUG();
 123        return -EINVAL;
 124}
 125
 126static void crypto_exit_ops(struct crypto_tfm *tfm)
 127{
 128        switch (crypto_tfm_alg_type(tfm)) {
 129        case CRYPTO_ALG_TYPE_CIPHER:
 130                crypto_exit_cipher_ops(tfm);
 131                break;
 132                
 133        case CRYPTO_ALG_TYPE_DIGEST:
 134                crypto_exit_digest_ops(tfm);
 135                break;
 136                
 137        case CRYPTO_ALG_TYPE_COMPRESS:
 138                crypto_exit_compress_ops(tfm);
 139                break;
 140        
 141        default:
 142                BUG();
 143                
 144        }
 145}
 146
 147static unsigned int crypto_ctxsize(struct crypto_alg *alg, int flags)
 148{
 149        unsigned int len;
 150
 151        switch (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) {
 152        default:
 153                BUG();
 154
 155        case CRYPTO_ALG_TYPE_CIPHER:
 156                len = crypto_cipher_ctxsize(alg, flags);
 157                break;
 158                
 159        case CRYPTO_ALG_TYPE_DIGEST:
 160                len = crypto_digest_ctxsize(alg, flags);
 161                break;
 162                
 163        case CRYPTO_ALG_TYPE_COMPRESS:
 164                len = crypto_compress_ctxsize(alg, flags);
 165                break;
 166        }
 167
 168        return len + (alg->cra_alignmask & ~(crypto_tfm_ctx_alignment() - 1));
 169}
 170
 171struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags)
 172{
 173        struct crypto_tfm *tfm = NULL;
 174        struct crypto_alg *alg;
 175        unsigned int tfm_size;
 176
 177        alg = crypto_alg_mod_lookup(name);
 178        if (alg == NULL)
 179                goto out;
 180
 181        tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, flags);
 182        tfm = kzalloc(tfm_size, GFP_KERNEL);
 183        if (tfm == NULL)
 184                goto out_put;
 185
 186        tfm->__crt_alg = alg;
 187        
 188        if (crypto_init_flags(tfm, flags))
 189                goto out_free_tfm;
 190                
 191        if (crypto_init_ops(tfm))
 192                goto out_free_tfm;
 193
 194        if (alg->cra_init && alg->cra_init(tfm))
 195                goto cra_init_failed;
 196
 197        goto out;
 198
 199cra_init_failed:
 200        crypto_exit_ops(tfm);
 201out_free_tfm:
 202        kfree(tfm);
 203        tfm = NULL;
 204out_put:
 205        crypto_alg_put(alg);
 206out:
 207        return tfm;
 208}
 209
 210void crypto_free_tfm(struct crypto_tfm *tfm)
 211{
 212        struct crypto_alg *alg;
 213        int size;
 214
 215        if (unlikely(!tfm))
 216                return;
 217
 218        alg = tfm->__crt_alg;
 219        size = sizeof(*tfm) + alg->cra_ctxsize;
 220
 221        if (alg->cra_exit)
 222                alg->cra_exit(tfm);
 223        crypto_exit_ops(tfm);
 224        crypto_alg_put(alg);
 225        memset(tfm, 0, size);
 226        kfree(tfm);
 227}
 228
 229static inline int crypto_set_driver_name(struct crypto_alg *alg)
 230{
 231        static const char suffix[] = "-generic";
 232        char *driver_name = alg->cra_driver_name;
 233        int len;
 234
 235        if (*driver_name)
 236                return 0;
 237
 238        len = strlcpy(driver_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
 239        if (len + sizeof(suffix) > CRYPTO_MAX_ALG_NAME)
 240                return -ENAMETOOLONG;
 241
 242        memcpy(driver_name + len, suffix, sizeof(suffix));
 243        return 0;
 244}
 245
 246int crypto_register_alg(struct crypto_alg *alg)
 247{
 248        int ret;
 249        struct crypto_alg *q;
 250
 251        if (alg->cra_alignmask & (alg->cra_alignmask + 1))
 252                return -EINVAL;
 253
 254        if (alg->cra_alignmask & alg->cra_blocksize)
 255                return -EINVAL;
 256
 257        if (alg->cra_blocksize > PAGE_SIZE / 8)
 258                return -EINVAL;
 259
 260        if (alg->cra_priority < 0)
 261                return -EINVAL;
 262        
 263        ret = crypto_set_driver_name(alg);
 264        if (unlikely(ret))
 265                return ret;
 266
 267        down_write(&crypto_alg_sem);
 268        
 269        list_for_each_entry(q, &crypto_alg_list, cra_list) {
 270                if (q == alg) {
 271                        ret = -EEXIST;
 272                        goto out;
 273                }
 274        }
 275        
 276        list_add(&alg->cra_list, &crypto_alg_list);
 277out:    
 278        up_write(&crypto_alg_sem);
 279        return ret;
 280}
 281
 282int crypto_unregister_alg(struct crypto_alg *alg)
 283{
 284        int ret = -ENOENT;
 285        struct crypto_alg *q;
 286        
 287        BUG_ON(!alg->cra_module);
 288        
 289        down_write(&crypto_alg_sem);
 290        list_for_each_entry(q, &crypto_alg_list, cra_list) {
 291                if (alg == q) {
 292                        list_del(&alg->cra_list);
 293                        ret = 0;
 294                        goto out;
 295                }
 296        }
 297out:    
 298        up_write(&crypto_alg_sem);
 299        return ret;
 300}
 301
 302int crypto_alg_available(const char *name, u32 flags)
 303{
 304        int ret = 0;
 305        struct crypto_alg *alg = crypto_alg_mod_lookup(name);
 306        
 307        if (alg) {
 308                crypto_alg_put(alg);
 309                ret = 1;
 310        }
 311        
 312        return ret;
 313}
 314
 315static int __init init_crypto(void)
 316{
 317        printk(KERN_INFO "Initializing Cryptographic API\n");
 318        crypto_init_proc();
 319        return 0;
 320}
 321
 322__initcall(init_crypto);
 323
 324EXPORT_SYMBOL_GPL(crypto_register_alg);
 325EXPORT_SYMBOL_GPL(crypto_unregister_alg);
 326EXPORT_SYMBOL_GPL(crypto_alloc_tfm);
 327EXPORT_SYMBOL_GPL(crypto_free_tfm);
 328EXPORT_SYMBOL_GPL(crypto_alg_available);
 329
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.