/* * Cryptographic API for algorithms (i.e., low-level API). * * Copyright (c) 2006 Herbert Xu * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * */ #include #include #include #include #include #include #include #include #include #include "internal.h" static LIST_HEAD(crypto_alg_list); static DECLARE_RWSEM(crypto_alg_sem); static unsigned crypto_ctxsize(struct crypto_alg *alg, u32 type, u32 mask) { return alg->cra_type->ctxsize(alg, type, mask); } unsigned crypto_alg_extsize(struct crypto_alg *alg) { return alg->cra_ctxsize; } struct crypto_alg *crypto_alg_mod_lookup(const char *name, u32 type, u32 mask) { struct crypto_alg *alg; down_read(&crypto_alg_sem); list_for_each_entry(alg, &crypto_alg_list, cra_list) if (!((alg->cra_flags ^ type) & mask) && !strcmp(alg->cra_name, name)) goto found; alg = ERR_PTR(-ENOENT); found: up_read(&crypto_alg_sem); return alg; } static void crypto_exit_ops(struct crypto_tfm *tfm) { if (tfm->exit) tfm->exit(tfm); } static struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type, u32 mask) { struct crypto_tfm *tfm = NULL; unsigned tfm_size; int err = -ENOMEM; tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, type, mask); tfm = kzalloc(tfm_size, GFP_KERNEL); if (tfm == NULL) return ERR_PTR(-ENOMEM); tfm->__crt_alg = alg; err = alg->cra_type->init(tfm, type, mask); if (err) goto out_free_tfm; if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm))) goto cra_init_failed; return tfm; cra_init_failed: crypto_exit_ops(tfm); out_free_tfm: kfree(tfm); return ERR_PTR(err); } struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask) { struct crypto_alg *alg; struct crypto_tfm *tfm; alg = crypto_alg_mod_lookup(alg_name, type, mask); if (IS_ERR(alg)) { fprintf(stderr, "unknown cipher %s\n", alg_name); return ERR_CAST(alg); } tfm = __crypto_alloc_tfm(alg, type, mask); if (IS_ERR(tfm)) return tfm; return tfm; } static void *crypto_create_tfm(struct crypto_alg *alg, const struct crypto_type *frontend) { struct crypto_tfm *tfm = NULL; unsigned tfmsize; unsigned total; void *mem; int err = -ENOMEM; tfmsize = frontend->tfmsize; total = tfmsize + sizeof(*tfm) + frontend->extsize(alg); mem = kzalloc(total, GFP_KERNEL); if (!mem) goto out_err; tfm = mem + tfmsize; tfm->__crt_alg = alg; err = frontend->init_tfm(tfm); if (err) goto out_free_tfm; if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm))) goto cra_init_failed; goto out; cra_init_failed: crypto_exit_ops(tfm); out_free_tfm: kfree(mem); out_err: mem = ERR_PTR(err); out: return mem; } static struct crypto_alg *crypto_find_alg(const char *alg_name, const struct crypto_type *frontend, u32 type, u32 mask) { if (frontend) { type &= frontend->maskclear; mask &= frontend->maskclear; type |= frontend->type; mask |= frontend->maskset; } return crypto_alg_mod_lookup(alg_name, type, mask); } void *crypto_alloc_tfm(const char *alg_name, const struct crypto_type *frontend, u32 type, u32 mask) { struct crypto_alg *alg; void *tfm; alg = crypto_find_alg(alg_name, frontend, type, mask); if (IS_ERR(alg)) return ERR_CAST(alg); tfm = crypto_create_tfm(alg, frontend); if (IS_ERR(tfm)) return tfm; return tfm; } void crypto_destroy_tfm(void *mem, struct crypto_tfm *tfm) { struct crypto_alg *alg; if (unlikely(!mem)) return; alg = tfm->__crt_alg; if (!tfm->exit && alg->cra_exit) alg->cra_exit(tfm); crypto_exit_ops(tfm); kzfree(mem); } int crypto_register_alg(struct crypto_alg *alg) { INIT_LIST_HEAD(&alg->cra_users); down_write(&crypto_alg_sem); list_add(&alg->cra_list, &crypto_alg_list); up_write(&crypto_alg_sem); return 0; } /* skcipher: */ static int crypto_skcipher_init_tfm(struct crypto_tfm *tfm) { struct crypto_skcipher *skcipher = __crypto_skcipher_cast(tfm); struct skcipher_alg *alg = crypto_skcipher_alg(skcipher); skcipher->setkey = alg->setkey; skcipher->encrypt = alg->encrypt; skcipher->decrypt = alg->decrypt; skcipher->ivsize = alg->ivsize; skcipher->keysize = alg->max_keysize; if (alg->init) return alg->init(skcipher); return 0; } static const struct crypto_type crypto_skcipher_type2 = { .extsize = crypto_alg_extsize, .init_tfm = crypto_skcipher_init_tfm, .maskclear = ~CRYPTO_ALG_TYPE_MASK, .maskset = CRYPTO_ALG_TYPE_BLKCIPHER_MASK, .tfmsize = offsetof(struct crypto_skcipher, base), }; struct crypto_skcipher *crypto_alloc_skcipher(const char *alg_name, u32 type, u32 mask) { return crypto_alloc_tfm(alg_name, &crypto_skcipher_type2, type, mask); }