From: Tudor Ambarus <tudor.ambarus@microchip.com>
To: "Stephan Müller" <smueller@chronox.de>, herbert@gondor.apana.org.au
Cc: <linux-crypto@vger.kernel.org>
Subject: Re: [PATCH v8 3/4] crypto: AF_ALG -- add asymmetric cipher
Date: Fri, 11 Aug 2017 15:51:10 +0300 [thread overview]
Message-ID: <8450990a-61bb-0f7c-70dd-643a45220d3f@microchip.com> (raw)
In-Reply-To: <2379311.RoATi6cCiZ@positron.chronox.de>
Hi, Stephan,
On 08/10/2017 09:40 AM, Stephan Müller wrote:
> This patch adds the user space interface for asymmetric ciphers. The
> interface allows the use of sendmsg as well as vmsplice to provide data.
>
> The akcipher interface implementation uses the common AF_ALG interface
> code regarding TX and RX SGL handling.
>
> Signed-off-by: Stephan Mueller <smueller@chronox.de>
> ---
> crypto/algif_akcipher.c | 466 ++++++++++++++++++++++++++++++++++++++++++++++++
> include/crypto/if_alg.h | 2 +
> 2 files changed, 468 insertions(+)
> create mode 100644 crypto/algif_akcipher.c
>
> diff --git a/crypto/algif_akcipher.c b/crypto/algif_akcipher.c
> new file mode 100644
> index 000000000000..1b36eb0b6e8f
> --- /dev/null
> +++ b/crypto/algif_akcipher.c
> @@ -0,0 +1,466 @@
> +/*
> + * algif_akcipher: User-space interface for asymmetric cipher algorithms
> + *
> + * Copyright (C) 2017, Stephan Mueller <smueller@chronox.de>
> + *
> + * This file provides the user-space API for asymmetric ciphers.
> + *
> + * 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.
> + *
> + * The following concept of the memory management is used:
> + *
> + * The kernel maintains two SGLs, the TX SGL and the RX SGL. The TX SGL is
> + * filled by user space with the data submitted via sendpage/sendmsg. Filling
> + * up the TX SGL does not cause a crypto operation -- the data will only be
> + * tracked by the kernel. Upon receipt of one recvmsg call, the caller must
> + * provide a buffer which is tracked with the RX SGL.
> + *
> + * During the processing of the recvmsg operation, the cipher request is
> + * allocated and prepared. As part of the recvmsg operation, the processed
> + * TX buffers are extracted from the TX SGL into a separate SGL.
> + *
> + * After the completion of the crypto operation, the RX SGL and the cipher
> + * request is released. The extracted TX SGL parts are released together with
> + * the RX SGL release.
> + */
> +
> +#include <crypto/akcipher.h>
> +#include <crypto/scatterwalk.h>
> +#include <crypto/if_alg.h>
> +#include <linux/init.h>
> +#include <linux/list.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/net.h>
> +#include <net/sock.h>
I like that you ordered these alphabetically. Is there a reason why
crypto/scatterwalk.h is not after crypto/if_alg.h?
> +
> +struct akcipher_tfm {
> + struct crypto_akcipher *akcipher;
> + bool has_key;
> +};
> +
> +static int akcipher_sendmsg(struct socket *sock, struct msghdr *msg,
> + size_t size)
> +{
> + return af_alg_sendmsg(sock, msg, size, 0);
> +}
> +
> +static int _akcipher_recvmsg(struct socket *sock, struct msghdr *msg,
> + size_t ignored, int flags)
> +{
> + struct sock *sk = sock->sk;
> + struct alg_sock *ask = alg_sk(sk);
> + struct sock *psk = ask->parent;
> + struct alg_sock *pask = alg_sk(psk);
> + struct af_alg_ctx *ctx = ask->private;
> + struct akcipher_tfm *akc = pask->private;
> + struct crypto_akcipher *tfm = akc->akcipher;
> + struct af_alg_async_req *areq;
> + int err = 0;
initialization not needed.
> + int maxsize;
> + size_t len = 0;
initialization not needed. len will be initialized in af_alg_get_rsgl.
Also, size_t could be 32 or 64 bit wide. Could you declare the size_t
variables before the int ones? This will avoid stack padding on 64bit
systems if one adds a new int variable in the same place where the ints
are now.
> + size_t used = 0;
initialization to zero not needed. You can directly initialize to
ctx->used or don't initialize at all.
> +
> + maxsize = crypto_akcipher_maxsize(tfm);
> + if (maxsize < 0)
> + return maxsize;
> +
> + /* Allocate cipher request for current operation. */
> + areq = af_alg_alloc_areq(sk, sizeof(struct af_alg_async_req) +
> + crypto_akcipher_reqsize(tfm));
> + if (IS_ERR(areq))
> + return PTR_ERR(areq);
> +
> + /* convert iovecs of output buffers into RX SGL */
> + err = af_alg_get_rsgl(sk, msg, flags, areq, maxsize, &len);
> + if (err)
> + goto free;
> +
> + /* ensure output buffer is sufficiently large */
> + if (len < maxsize) {
> + err = -EMSGSIZE;
> + goto free;
> + }
> +
> + /*
> + * Create a per request TX SGL for this request which tracks the
> + * SG entries from the global TX SGL.
> + */
> + used = ctx->used;
> + areq->tsgl_entries = af_alg_count_tsgl(sk, used, 0);
> + if (!areq->tsgl_entries)
> + areq->tsgl_entries = 1;
> + areq->tsgl = sock_kmalloc(sk, sizeof(*areq->tsgl) * areq->tsgl_entries,
> + GFP_KERNEL);
> + if (!areq->tsgl) {
> + err = -ENOMEM;
> + goto free;
> + }
> + sg_init_table(areq->tsgl, areq->tsgl_entries);
> + af_alg_pull_tsgl(sk, used, areq->tsgl, 0);
> +
> + /* Initialize the crypto operation */
> + akcipher_request_set_tfm(&areq->cra_u.akcipher_req, tfm);
> + akcipher_request_set_crypt(&areq->cra_u.akcipher_req, areq->tsgl,
> + areq->first_rsgl.sgl.sg, used, len);
> +
> + if (msg->msg_iocb && !is_sync_kiocb(msg->msg_iocb)) {
> + /* AIO operation */
> + areq->iocb = msg->msg_iocb;
> + akcipher_request_set_callback(&areq->cra_u.akcipher_req,
> + CRYPTO_TFM_REQ_MAY_SLEEP,
> + af_alg_async_cb, areq);
> + } else
Unbalanced braces around else statement.
> + /* Synchronous operation */
> + akcipher_request_set_callback(&areq->cra_u.akcipher_req,
> + CRYPTO_TFM_REQ_MAY_SLEEP |
> + CRYPTO_TFM_REQ_MAY_BACKLOG,
> + af_alg_complete,
> + &ctx->completion);
> +
> + switch (ctx->op) {
> + case ALG_OP_ENCRYPT:
> + err = crypto_akcipher_encrypt(&areq->cra_u.akcipher_req);
> + break;
> + case ALG_OP_DECRYPT:
> + err = crypto_akcipher_decrypt(&areq->cra_u.akcipher_req);
> + break;
> + case ALG_OP_SIGN:
> + err = crypto_akcipher_sign(&areq->cra_u.akcipher_req);
> + break;
> + case ALG_OP_VERIFY:
> + err = crypto_akcipher_verify(&areq->cra_u.akcipher_req);
> + break;
> + default:
> + err = -EOPNOTSUPP;
> + goto free;
> + }
> +
> + /* Wait for synchronous operation completion */
> + if (msg->msg_iocb && !is_sync_kiocb(msg->msg_iocb))
> + err = af_alg_wait_for_completion(err, &ctx->completion);
> +
> + /* AIO operation in progress */
> + if (err == -EINPROGRESS) {
> + sock_hold(sk);
> +
> + /* Remember output size that will be generated. */
> + areq->outlen = areq->cra_u.akcipher_req.dst_len;
don't you have the dst_len value in len variable?
> +
> + return -EIOCBQUEUED;
> + }
> +
> +free:
> + af_alg_free_areq_sgls(areq);
> + sock_kfree_s(sk, areq, areq->areqlen);
> +
> + return err ? err : areq->cra_u.akcipher_req.dst_len;
> +}
> +
> +static int akcipher_recvmsg(struct socket *sock, struct msghdr *msg,
> + size_t ignored, int flags)
> +{
> + struct sock *sk = sock->sk;
> + struct alg_sock *ask = alg_sk(sk);
> + struct sock *psk = ask->parent;
> + struct alg_sock *pask = alg_sk(psk);
> + struct akcipher_tfm *akc = pask->private;
> + struct crypto_akcipher *tfm = akc->akcipher;
> +
blank line
> + int ret = 0;
> +
> + lock_sock(sk);
> +
> + while (msg_data_left(msg)) {
> + int err = _akcipher_recvmsg(sock, msg, ignored, flags);
If the compiler will not make any optimization, you will end up in
allocating an int on the stack for each iteration.
> +
> + /*
> + * This error covers -EIOCBQUEUED which implies that we can
> + * only handle one AIO request. If the caller wants to have
> + * multiple AIO requests in parallel, he must make multiple
> + * separate AIO calls.
> + */
> + if (err <= 0) {
why the equal?
> + if (err == -EIOCBQUEUED || err == -EBADMSG || !ret)
> + ret = err;
> + goto out;
> + }
> +
> + ret += err;
> +
> + /*
> + * The caller must provide crypto_akcipher_maxsize per request.
> + * If he provides more, we conclude that multiple akcipher
> + * operations are requested.
> + */
> + iov_iter_advance(&msg->msg_iter,
> + crypto_akcipher_maxsize(tfm) - err);
> + }
> +
> +out:
> + af_alg_wmem_wakeup(sk);
> + release_sock(sk);
> + return ret;
> +}
> +
> +static struct proto_ops algif_akcipher_ops = {
static const struct?
> + .family = PF_ALG,
> +
> + .connect = sock_no_connect,
> + .socketpair = sock_no_socketpair,
> + .getname = sock_no_getname,
> + .ioctl = sock_no_ioctl,
> + .listen = sock_no_listen,
> + .shutdown = sock_no_shutdown,
> + .getsockopt = sock_no_getsockopt,
> + .mmap = sock_no_mmap,
> + .bind = sock_no_bind,
> + .accept = sock_no_accept,
> + .setsockopt = sock_no_setsockopt,
> +
> + .release = af_alg_release,
> + .sendmsg = akcipher_sendmsg,
> + .sendpage = af_alg_sendpage,
> + .recvmsg = akcipher_recvmsg,
> + .poll = af_alg_poll,
> +};
> +
> +static int akcipher_check_key(struct socket *sock)
> +{
> + int err = 0;
initialization not needed.
You can avoid stack padding on 64bit systems if you move the int after
pointers.
> + struct sock *psk;
> + struct alg_sock *pask;
> + struct akcipher_tfm *tfm;
> + struct sock *sk = sock->sk;
> + struct alg_sock *ask = alg_sk(sk);
> +
> + lock_sock(sk);
> + if (ask->refcnt)
> + goto unlock_child;
> +
> + psk = ask->parent;
> + pask = alg_sk(ask->parent);
> + tfm = pask->private;
> +
> + err = -ENOKEY;
see https://rusty.ozlabs.org/?p=232
> + lock_sock_nested(psk, SINGLE_DEPTH_NESTING);
> + if (!tfm->has_key)
> + goto unlock;
> +
> + if (!pask->refcnt++)
> + sock_hold(psk);
> +
> + ask->refcnt = 1;
> + sock_put(psk);
> +
> + err = 0;
> +
> +unlock:
> + release_sock(psk);
> +unlock_child:
> + release_sock(sk);
> +
> + return err;
> +}
> +
> +static int akcipher_sendmsg_nokey(struct socket *sock, struct msghdr *msg,
> + size_t size)
> +{
> + int err;
> +
> + err = akcipher_check_key(sock);
> + if (err)
> + return err;
> +
> + return akcipher_sendmsg(sock, msg, size);
> +}
> +
> +static ssize_t akcipher_sendpage_nokey(struct socket *sock, struct page *page,
> + int offset, size_t size, int flags)
> +{
> + int err;
> +
> + err = akcipher_check_key(sock);
> + if (err)
> + return err;
> +
> + return af_alg_sendpage(sock, page, offset, size, flags);
> +}
> +
> +static int akcipher_recvmsg_nokey(struct socket *sock, struct msghdr *msg,
> + size_t ignored, int flags)
> +{
> + int err;
> +
> + err = akcipher_check_key(sock);
> + if (err)
> + return err;
> +
> + return akcipher_recvmsg(sock, msg, ignored, flags);
> +}
> +
> +static struct proto_ops algif_akcipher_ops_nokey = {
static const struct?
> + .family = PF_ALG,
> +
> + .connect = sock_no_connect,
> + .socketpair = sock_no_socketpair,
> + .getname = sock_no_getname,
> + .ioctl = sock_no_ioctl,
> + .listen = sock_no_listen,
> + .shutdown = sock_no_shutdown,
> + .getsockopt = sock_no_getsockopt,
> + .mmap = sock_no_mmap,
> + .bind = sock_no_bind,
> + .accept = sock_no_accept,
> + .setsockopt = sock_no_setsockopt,
> +
> + .release = af_alg_release,
> + .sendmsg = akcipher_sendmsg_nokey,
> + .sendpage = akcipher_sendpage_nokey,
> + .recvmsg = akcipher_recvmsg_nokey,
> + .poll = af_alg_poll,
> +};
> +
> +static void *akcipher_bind(const char *name, u32 type, u32 mask)
> +{
> + struct akcipher_tfm *tfm;
> + struct crypto_akcipher *akcipher;
> +
> + tfm = kzalloc(sizeof(*tfm), GFP_KERNEL);
> + if (!tfm)
> + return ERR_PTR(-ENOMEM);
> +
> + akcipher = crypto_alloc_akcipher(name, type, mask);
> + if (IS_ERR(akcipher)) {
> + kfree(tfm);
> + return ERR_CAST(akcipher);
> + }
> +
> + tfm->akcipher = akcipher;
tfm->akcipher was initialized to zero and now you overwrite it.
Maybe it's better to use kmalloc and tfm->has_key = false so you
can get rid of the superfluous initialization.
> +
> + return tfm;
> +}
> +
> +static void akcipher_release(void *private)
> +{
> + struct akcipher_tfm *tfm = private;
> + struct crypto_akcipher *akcipher = tfm->akcipher;
> +
> + crypto_free_akcipher(akcipher);
> + kfree(tfm);
> +}
> +
> +static int akcipher_setprivkey(void *private, const u8 *key,
> + unsigned int keylen)
> +{
> + struct akcipher_tfm *tfm = private;
> + struct crypto_akcipher *akcipher = tfm->akcipher;
> + int err;
> +
> + err = crypto_akcipher_set_priv_key(akcipher, key, keylen);
> + tfm->has_key = !err;
> +
> + /* Return the maximum size of the akcipher operation. */
> + if (!err)
> + err = crypto_akcipher_maxsize(akcipher);
crypto subsystem returns zero when setkey is successful and introduces
a new function for determining the maxsize. Should we comply with that?
> +
> + return err;
> +}
> +
> +static int akcipher_setpubkey(void *private, const u8 *key, unsigned int keylen)
> +{
> + struct akcipher_tfm *tfm = private;
> + struct crypto_akcipher *akcipher = tfm->akcipher;
> + int err;
> +
> + err = crypto_akcipher_set_pub_key(akcipher, key, keylen);
> + tfm->has_key = !err;
> +
> + /* Return the maximum size of the akcipher operation. */
> + if (!err)
> + err = crypto_akcipher_maxsize(akcipher);
> +
> + return err;
> +}
> +
> +static void akcipher_sock_destruct(struct sock *sk)
> +{
> + struct alg_sock *ask = alg_sk(sk);
> + struct af_alg_ctx *ctx = ask->private;
> +
> + af_alg_pull_tsgl(sk, ctx->used, NULL, 0);
> + sock_kfree_s(sk, ctx, ctx->len);
> + af_alg_release_parent(sk);
> +}
> +
> +static int akcipher_accept_parent_nokey(void *private, struct sock *sk)
> +{
> + struct af_alg_ctx *ctx;
> + struct alg_sock *ask = alg_sk(sk);
> + unsigned int len = sizeof(*ctx);
> +
> + ctx = sock_kmalloc(sk, len, GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> + memset(ctx, 0, len);
do you really need the memset? you can initialize the members that you
will use as you did below.
> +
> + INIT_LIST_HEAD(&ctx->tsgl_list);
> + ctx->len = len;
> + ctx->used = 0;
> + ctx->rcvused = 0;
> + ctx->more = 0;
> + ctx->merge = 0;
> + ctx->op = 0;
> + af_alg_init_completion(&ctx->completion);
> +
> + ask->private = ctx;
> +
> + sk->sk_destruct = akcipher_sock_destruct;
> +
> + return 0;
> +}
> +
> +static int akcipher_accept_parent(void *private, struct sock *sk)
> +{
> + struct akcipher_tfm *tfm = private;
> +
> + if (!tfm->has_key)
> + return -ENOKEY;
> +
> + return akcipher_accept_parent_nokey(private, sk);
> +}
> +
> +static const struct af_alg_type algif_type_akcipher = {
> + .bind = akcipher_bind,
> + .release = akcipher_release,
> + .setkey = akcipher_setprivkey,
> + .setpubkey = akcipher_setpubkey,
> + .setauthsize = NULL,
> + .accept = akcipher_accept_parent,
> + .accept_nokey = akcipher_accept_parent_nokey,
> + .ops = &algif_akcipher_ops,
> + .ops_nokey = &algif_akcipher_ops_nokey,
> + .name = "akcipher",
> + .owner = THIS_MODULE
> +};
> +
> +static int __init algif_akcipher_init(void)
> +{
> + return af_alg_register_type(&algif_type_akcipher);
> +}
> +
> +static void __exit algif_akcipher_exit(void)
> +{
> + int err = af_alg_unregister_type(&algif_type_akcipher);
checkpatch suggests to insert a blank line after declaration.
Cheers,
ta
> + BUG_ON(err);
> +}
> +
> +module_init(algif_akcipher_init);
> +module_exit(algif_akcipher_exit);
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
> +MODULE_DESCRIPTION("Asymmetric kernel crypto API user space interface");
> diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h
> index d1de8ed3e77b..a2b396756e24 100644
> --- a/include/crypto/if_alg.h
> +++ b/include/crypto/if_alg.h
> @@ -22,6 +22,7 @@
>
> #include <crypto/aead.h>
> #include <crypto/skcipher.h>
> +#include <crypto/akcipher.h>
>
> #define ALG_MAX_PAGES 16
>
> @@ -119,6 +120,7 @@ struct af_alg_async_req {
> union {
> struct aead_request aead_req;
> struct skcipher_request skcipher_req;
> + struct akcipher_request akcipher_req;
> } cra_u;
>
> /* req ctx trails this struct */
>
next prev parent reply other threads:[~2017-08-11 12:51 UTC|newest]
Thread overview: 39+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-08-10 6:39 [PATCH v8 0/4] crypto: add algif_akcipher user space API Stephan Müller
2017-08-10 6:39 ` [PATCH v8 1/4] crypto: AF_ALG -- add sign/verify API Stephan Müller
2017-08-10 12:49 ` Tudor Ambarus
2017-08-10 13:03 ` Stephan Mueller
2017-08-10 13:59 ` Tudor Ambarus
2017-08-10 14:06 ` Stephan Müller
2017-08-10 6:39 ` [PATCH v8 2/4] crypto: AF_ALG -- add setpubkey setsockopt call Stephan Müller
2017-08-10 6:40 ` [PATCH v8 3/4] crypto: AF_ALG -- add asymmetric cipher Stephan Müller
2017-08-11 12:51 ` Tudor Ambarus [this message]
2017-08-19 13:53 ` Stephan Müller
2017-08-21 8:55 ` Tudor Ambarus
2017-08-21 9:23 ` Tudor Ambarus
2017-08-21 9:39 ` Stephan Mueller
2017-08-10 6:40 ` [PATCH v8 4/4] crypto: algif_akcipher - enable compilation Stephan Müller
2017-08-11 12:56 ` Tudor Ambarus
2017-08-11 13:03 ` Stephan Mueller
2017-08-11 0:48 ` [PATCH v8 0/4] crypto: add algif_akcipher user space API Mat Martineau
2017-08-11 5:13 ` Marcel Holtmann
2017-08-11 6:30 ` Stephan Müller
2017-08-11 16:02 ` Marcel Holtmann
2017-08-14 6:24 ` Stephan Mueller
2017-08-14 6:42 ` Marcel Holtmann
2017-08-11 7:18 ` Stephan Mueller
2017-08-11 16:05 ` Marcel Holtmann
2017-08-13 8:52 ` Gilad Ben-Yossef
2017-08-14 6:01 ` Stephan Mueller
2017-08-17 13:17 ` Tudor Ambarus
2017-08-30 6:15 ` Tudor Ambarus
2017-08-30 7:21 ` Marcel Holtmann
2017-08-30 8:17 ` Tudor Ambarus
2017-08-30 12:36 ` Marcel Holtmann
2017-08-11 10:18 ` Andrew Zaborowski
2017-08-11 19:43 ` Mat Martineau
2017-08-14 6:03 ` Stephan Mueller
2017-08-14 6:26 ` Marcel Holtmann
2017-08-14 7:23 ` Stephan Mueller
2017-08-14 9:26 ` Marcel Holtmann
2017-10-02 14:15 ` Tudor Ambarus
2017-10-03 0:09 ` Mat Martineau
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8450990a-61bb-0f7c-70dd-643a45220d3f@microchip.com \
--to=tudor.ambarus@microchip.com \
--cc=herbert@gondor.apana.org.au \
--cc=linux-crypto@vger.kernel.org \
--cc=smueller@chronox.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox