diff options
Diffstat (limited to 'crypto/akcipher.c')
-rw-r--r-- | crypto/akcipher.c | 124 |
1 files changed, 123 insertions, 1 deletions
diff --git a/crypto/akcipher.c b/crypto/akcipher.c index 7960ceb528c3..52813f0b19e4 100644 --- a/crypto/akcipher.c +++ b/crypto/akcipher.c @@ -10,6 +10,7 @@ #include <linux/errno.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/scatterlist.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/string.h> @@ -17,6 +18,8 @@ #include "internal.h" +#define CRYPTO_ALG_TYPE_AHASH_MASK 0x0000000e + static int __maybe_unused crypto_akcipher_report( struct sk_buff *skb, struct crypto_alg *alg) { @@ -105,7 +108,7 @@ static const struct crypto_type crypto_akcipher_type = { .report_stat = crypto_akcipher_report_stat, #endif .maskclear = ~CRYPTO_ALG_TYPE_MASK, - .maskset = CRYPTO_ALG_TYPE_MASK, + .maskset = CRYPTO_ALG_TYPE_AHASH_MASK, .type = CRYPTO_ALG_TYPE_AKCIPHER, .tfmsize = offsetof(struct crypto_akcipher, base), }; @@ -186,5 +189,124 @@ int akcipher_register_instance(struct crypto_template *tmpl, } EXPORT_SYMBOL_GPL(akcipher_register_instance); +int crypto_akcipher_sync_prep(struct crypto_akcipher_sync_data *data) +{ + unsigned int reqsize = crypto_akcipher_reqsize(data->tfm); + struct akcipher_request *req; + struct scatterlist *sg; + unsigned int mlen; + unsigned int len; + u8 *buf; + + if (data->dst) + mlen = max(data->slen, data->dlen); + else + mlen = data->slen + data->dlen; + + len = sizeof(*req) + reqsize + mlen; + if (len < mlen) + return -EOVERFLOW; + + req = kzalloc(len, GFP_KERNEL); + if (!req) + return -ENOMEM; + + data->req = req; + akcipher_request_set_tfm(req, data->tfm); + + buf = (u8 *)(req + 1) + reqsize; + data->buf = buf; + memcpy(buf, data->src, data->slen); + + sg = &data->sg; + sg_init_one(sg, buf, mlen); + akcipher_request_set_crypt(req, sg, data->dst ? sg : NULL, + data->slen, data->dlen); + + crypto_init_wait(&data->cwait); + akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &data->cwait); + + return 0; +} +EXPORT_SYMBOL_GPL(crypto_akcipher_sync_prep); + +int crypto_akcipher_sync_post(struct crypto_akcipher_sync_data *data, int err) +{ + err = crypto_wait_req(err, &data->cwait); + if (data->dst) + memcpy(data->dst, data->buf, data->dlen); + data->dlen = data->req->dst_len; + kfree_sensitive(data->req); + return err; +} +EXPORT_SYMBOL_GPL(crypto_akcipher_sync_post); + +int crypto_akcipher_sync_encrypt(struct crypto_akcipher *tfm, + const void *src, unsigned int slen, + void *dst, unsigned int dlen) +{ + struct crypto_akcipher_sync_data data = { + .tfm = tfm, + .src = src, + .dst = dst, + .slen = slen, + .dlen = dlen, + }; + + return crypto_akcipher_sync_prep(&data) ?: + crypto_akcipher_sync_post(&data, + crypto_akcipher_encrypt(data.req)); +} +EXPORT_SYMBOL_GPL(crypto_akcipher_sync_encrypt); + +int crypto_akcipher_sync_decrypt(struct crypto_akcipher *tfm, + const void *src, unsigned int slen, + void *dst, unsigned int dlen) +{ + struct crypto_akcipher_sync_data data = { + .tfm = tfm, + .src = src, + .dst = dst, + .slen = slen, + .dlen = dlen, + }; + + return crypto_akcipher_sync_prep(&data) ?: + crypto_akcipher_sync_post(&data, + crypto_akcipher_decrypt(data.req)) ?: + data.dlen; +} +EXPORT_SYMBOL_GPL(crypto_akcipher_sync_decrypt); + +static void crypto_exit_akcipher_ops_sig(struct crypto_tfm *tfm) +{ + struct crypto_akcipher **ctx = crypto_tfm_ctx(tfm); + + crypto_free_akcipher(*ctx); +} + +int crypto_init_akcipher_ops_sig(struct crypto_tfm *tfm) +{ + struct crypto_akcipher **ctx = crypto_tfm_ctx(tfm); + struct crypto_alg *calg = tfm->__crt_alg; + struct crypto_akcipher *akcipher; + + if (!crypto_mod_get(calg)) + return -EAGAIN; + + akcipher = crypto_create_tfm(calg, &crypto_akcipher_type); + if (IS_ERR(akcipher)) { + crypto_mod_put(calg); + return PTR_ERR(akcipher); + } + + *ctx = akcipher; + tfm->exit = crypto_exit_akcipher_ops_sig; + + return 0; +} +EXPORT_SYMBOL_GPL(crypto_init_akcipher_ops_sig); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic public key cipher type"); |