RHEL5/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_tfm2(const char *name, u32 flags,
 172                                     int nomodload)
 173{
 174        struct crypto_tfm *tfm = NULL;
 175        struct crypto_alg *alg;
 176        unsigned int tfm_size;
 177
 178        if (!nomodload)
 179                alg = crypto_alg_mod_lookup(name);
 180        else
 181                alg = crypto_alg_lookup(name);
 182
 183        if (alg == NULL)
 184                goto out;
 185
 186        tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, flags);
 187        tfm = kzalloc(tfm_size, GFP_KERNEL);
 188        if (tfm == NULL)
 189                goto out_put;
 190
 191        tfm->__crt_alg = alg;
 192        
 193        if (crypto_init_flags(tfm, flags))
 194                goto out_free_tfm;
 195                
 196        if (crypto_init_ops(tfm))
 197                goto out_free_tfm;
 198
 199        if (alg->cra_init && alg->cra_init(tfm))
 200                goto cra_init_failed;
 201
 202        goto out;
 203
 204cra_init_failed:
 205        crypto_exit_ops(tfm);
 206out_free_tfm:
 207        kfree(tfm);
 208        tfm = NULL;
 209out_put:
 210        crypto_alg_put(alg);
 211out:
 212        return tfm;
 213}
 214
 215struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags)
 216{
 217        return crypto_alloc_tfm2(name, flags, 0);
 218}
 219
 220void crypto_free_tfm(struct crypto_tfm *tfm)
 221{
 222        struct crypto_alg *alg;
 223        int size;
 224
 225        if (unlikely(!tfm))
 226                return;
 227
 228        alg = tfm->__crt_alg;
 229        size = sizeof(*tfm) + alg->cra_ctxsize;
 230
 231        if (alg->cra_exit)
 232                alg->cra_exit(tfm);
 233        crypto_exit_ops(tfm);
 234        crypto_alg_put(alg);
 235        memset(tfm, 0, size);
 236        kfree(tfm);
 237}
 238
 239static inline int crypto_set_driver_name(struct crypto_alg *alg)
 240{
 241        static const char suffix[] = "-generic";
 242        char *driver_name = alg->cra_driver_name;
 243        int len;
 244
 245        if (*driver_name)
 246                return 0;
 247
 248        len = strlcpy(driver_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
 249        if (len + sizeof(suffix) > CRYPTO_MAX_ALG_NAME)
 250                return -ENAMETOOLONG;
 251
 252        memcpy(driver_name + len, suffix, sizeof(suffix));
 253        return 0;
 254}
 255
 256int crypto_register_alg(struct crypto_alg *alg)
 257{
 258        int ret;
 259        struct crypto_alg *q;
 260
 261        if (alg->cra_alignmask & (alg->cra_alignmask + 1))
 262                return -EINVAL;
 263
 264        if (alg->cra_alignmask & alg->cra_blocksize)
 265                return -EINVAL;
 266
 267        if (alg->cra_blocksize > PAGE_SIZE / 8)
 268                return -EINVAL;
 269
 270        if (alg->cra_priority < 0)
 271                return -EINVAL;
 272        
 273        ret = crypto_set_driver_name(alg);
 274        if (unlikely(ret))
 275                return ret;
 276
 277        down_write(&crypto_alg_sem);
 278        
 279        list_for_each_entry(q, &crypto_alg_list, cra_list) {
 280                if (q == alg) {
 281                        ret = -EEXIST;
 282                        goto out;
 283                }
 284        }
 285        
 286        list_add(&alg->cra_list, &crypto_alg_list);
 287out:    
 288        up_write(&crypto_alg_sem);
 289        return ret;
 290}
 291
 292int crypto_unregister_alg(struct crypto_alg *alg)
 293{
 294        int ret = -ENOENT;
 295        struct crypto_alg *q;
 296        
 297        BUG_ON(!alg->cra_module);
 298        
 299        down_write(&crypto_alg_sem);
 300        list_for_each_entry(q, &crypto_alg_list, cra_list) {
 301                if (alg == q) {
 302                        list_del(&alg->cra_list);
 303                        ret = 0;
 304                        goto out;
 305                }
 306        }
 307out:    
 308        up_write(&crypto_alg_sem);
 309        return ret;
 310}
 311
 312int crypto_alg_available(const char *name, u32 flags)
 313{
 314        int ret = 0;
 315        struct crypto_alg *alg = crypto_alg_mod_lookup(name);
 316        
 317        if (alg) {
 318                crypto_alg_put(alg);
 319                ret = 1;
 320        }
 321        
 322        return ret;
 323}
 324
 325static int __init init_crypto(void)
 326{
 327        printk(KERN_INFO "Initializing Cryptographic API\n");
 328        crypto_init_proc();
 329        return 0;
 330}
 331
 332__initcall(init_crypto);
 333
 334EXPORT_SYMBOL_GPL(crypto_register_alg);
 335EXPORT_SYMBOL_GPL(crypto_unregister_alg);
 336EXPORT_SYMBOL_GPL(crypto_alloc_tfm);
 337EXPORT_SYMBOL_GPL(crypto_free_tfm);
 338EXPORT_SYMBOL_GPL(crypto_alg_available);
 339