summaryrefslogtreecommitdiff
path: root/linux/crypto/api.c
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2017-01-08 00:13:18 -0900
committerKent Overstreet <kent.overstreet@gmail.com>2017-01-20 09:07:08 -0900
commitb33fc8298f7e13226b9895abc57c9bfce5e3fa2d (patch)
treea3d2a5a909b6372f7777c1c5c18cef5f81d123a9 /linux/crypto/api.c
parent7f4191a202ea4558ca2d5eb8a47daea33c9999c7 (diff)
bcache in userspace; userspace fsck
Diffstat (limited to 'linux/crypto/api.c')
-rw-r--r--linux/crypto/api.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/linux/crypto/api.c b/linux/crypto/api.c
new file mode 100644
index 00000000..513a48aa
--- /dev/null
+++ b/linux/crypto/api.c
@@ -0,0 +1,326 @@
+/*
+ * Scatterlist Cryptographic API.
+ *
+ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ * Copyright (c) 2002 David S. Miller (davem@redhat.com)
+ * Copyright (c) 2005 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * Portions derived from Cryptoapi, by Alexander Kjeldaas <astor@fast.no>
+ * and Nettle, by Niels Möller.
+ *
+ * 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 <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "internal.h"
+
+LIST_HEAD(crypto_alg_list);
+DECLARE_RWSEM(crypto_alg_sem);
+
+static struct crypto_alg *__crypto_alg_lookup(const char *name, u32 type,
+ u32 mask)
+{
+ struct crypto_alg *q, *alg = NULL;
+ int best = -2;
+
+ list_for_each_entry(q, &crypto_alg_list, cra_list) {
+ int exact, fuzzy;
+
+ if ((q->cra_flags ^ type) & mask)
+ continue;
+
+ exact = !strcmp(q->cra_driver_name, name);
+ fuzzy = !strcmp(q->cra_name, name);
+ if (!exact && !(fuzzy && q->cra_priority > best))
+ continue;
+
+ if (unlikely(!crypto_alg_get(q)))
+ continue;
+
+ best = q->cra_priority;
+ if (alg)
+ crypto_alg_put(alg);
+ alg = q;
+
+ if (exact)
+ break;
+ }
+
+ return alg;
+}
+
+struct crypto_alg *crypto_alg_mod_lookup(const char *name, u32 type, u32 mask)
+{
+ struct crypto_alg *alg;
+
+ /*
+ * If the internal flag is set for a cipher, require a caller to
+ * to invoke the cipher with the internal flag to use that cipher.
+ * Also, if a caller wants to allocate a cipher that may or may
+ * not be an internal cipher, use type | CRYPTO_ALG_INTERNAL and
+ * !(mask & CRYPTO_ALG_INTERNAL).
+ */
+ if (!((type | mask) & CRYPTO_ALG_INTERNAL))
+ mask |= CRYPTO_ALG_INTERNAL;
+
+ down_read(&crypto_alg_sem);
+ alg = __crypto_alg_lookup(name, type, mask);
+ up_read(&crypto_alg_sem);
+
+ return alg ?: ERR_PTR(-ENOENT);
+}
+
+static int crypto_init_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
+{
+ const struct crypto_type *type_obj = tfm->__crt_alg->cra_type;
+
+ if (type_obj)
+ return type_obj->init(tfm, type, mask);
+
+ switch (crypto_tfm_alg_type(tfm)) {
+ case CRYPTO_ALG_TYPE_CIPHER:
+ return crypto_init_cipher_ops(tfm);
+ default:
+ break;
+ }
+
+ BUG();
+ return -EINVAL;
+}
+
+static void crypto_exit_ops(struct crypto_tfm *tfm)
+{
+ const struct crypto_type *type = tfm->__crt_alg->cra_type;
+
+ if (type) {
+ if (tfm->exit)
+ tfm->exit(tfm);
+ return;
+ }
+
+ switch (crypto_tfm_alg_type(tfm)) {
+ case CRYPTO_ALG_TYPE_CIPHER:
+ crypto_exit_cipher_ops(tfm);
+ break;
+
+ default:
+ BUG();
+ }
+}
+
+static unsigned int crypto_ctxsize(struct crypto_alg *alg, u32 type, u32 mask)
+{
+ const struct crypto_type *type_obj = alg->cra_type;
+ unsigned int len;
+
+ len = alg->cra_alignmask & ~(crypto_tfm_ctx_alignment() - 1);
+ if (type_obj)
+ return len + type_obj->ctxsize(alg, type, mask);
+
+ switch (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) {
+ default:
+ BUG();
+
+ case CRYPTO_ALG_TYPE_CIPHER:
+ len += crypto_cipher_ctxsize(alg);
+ break;
+ }
+
+ return len;
+}
+
+struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
+ u32 mask)
+{
+ struct crypto_tfm *tfm = NULL;
+ unsigned int tfm_size;
+ int err = -ENOMEM;
+
+ tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, type, mask);
+ tfm = kzalloc(tfm_size, GFP_KERNEL);
+ if (tfm == NULL)
+ goto out_err;
+
+ tfm->__crt_alg = alg;
+
+ err = crypto_init_ops(tfm, type, mask);
+ 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(tfm);
+out_err:
+ tfm = ERR_PTR(err);
+out:
+ return tfm;
+}
+
+/*
+ * crypto_alloc_base - Locate algorithm and allocate transform
+ * @alg_name: Name of algorithm
+ * @type: Type of algorithm
+ * @mask: Mask for type comparison
+ *
+ * This function should not be used by new algorithm types.
+ * Please use crypto_alloc_tfm instead.
+ *
+ * crypto_alloc_base() will first attempt to locate an already loaded
+ * algorithm. If that fails and the kernel supports dynamically loadable
+ * modules, it will then attempt to load a module of the same name or
+ * alias. If that fails it will send a query to any loaded crypto manager
+ * to construct an algorithm on the fly. A refcount is grabbed on the
+ * algorithm which is then associated with the new transform.
+ *
+ * The returned transform is of a non-determinate type. Most people
+ * should use one of the more specific allocation functions such as
+ * crypto_alloc_blkcipher.
+ *
+ * In case of error the return value is an error pointer.
+ */
+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)) {
+ crypto_alg_put(alg);
+ return tfm;
+ }
+
+ return tfm;
+}
+
+void *crypto_create_tfm(struct crypto_alg *alg,
+ const struct crypto_type *frontend)
+{
+ char *mem;
+ struct crypto_tfm *tfm = NULL;
+ unsigned int tfmsize;
+ unsigned int total;
+ int err = -ENOMEM;
+
+ tfmsize = frontend->tfmsize;
+ total = tfmsize + sizeof(*tfm) + frontend->extsize(alg);
+
+ mem = kzalloc(total, GFP_KERNEL);
+ if (mem == NULL)
+ goto out_err;
+
+ tfm = (struct crypto_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;
+}
+
+struct crypto_alg *crypto_find_alg(const char *alg_name,
+ const struct crypto_type *frontend,
+ u32 type, u32 mask)
+{
+ struct crypto_alg *(*lookup)(const char *name, u32 type, u32 mask) =
+ crypto_alg_mod_lookup;
+
+ if (frontend) {
+ type &= frontend->maskclear;
+ mask &= frontend->maskclear;
+ type |= frontend->type;
+ mask |= frontend->maskset;
+
+ if (frontend->lookup)
+ lookup = frontend->lookup;
+ }
+
+ return 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)) {
+ crypto_alg_put(alg);
+ 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);
+ crypto_alg_put(alg);
+ kzfree(mem);
+}
+
+int crypto_has_alg(const char *name, u32 type, u32 mask)
+{
+ int ret = 0;
+ struct crypto_alg *alg = crypto_alg_mod_lookup(name, type, mask);
+
+ if (!IS_ERR(alg)) {
+ crypto_alg_put(alg);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+MODULE_DESCRIPTION("Cryptographic core API");
+MODULE_LICENSE("GPL");