lkml.org 
[lkml]   [2016]   [May]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    From
    SubjectRe: [PATCH RESEND v5 6/6] crypto: AF_ALG - add support for key_id
    Date
    Am Donnerstag, 5. Mai 2016, 12:51:20 schrieb Tadeusz Struk:

    Hi Tadeusz,

    > This patch adds support for asymmetric key type to AF_ALG.
    > It will work as follows: A new PF_ALG socket options are
    > added on top of existing ALG_SET_KEY and ALG_SET_PUBKEY, namely
    > ALG_SET_KEY_ID and ALG_SET_PUBKEY_ID for setting public and
    > private keys respectively. When these new options will be used
    > the user, instead of providing the key material, will provide a
    > key id and the key itself will be obtained from kernel keyring
    > subsystem. The user will use the standard tools (keyctl tool
    > or the keyctl syscall) for key instantiation and to obtain the
    > key id. The key id can also be obtained by reading the
    > /proc/keys file.
    >
    > When a key corresponding to the given keyid is found, it is stored
    > in the socket context and subsequent crypto operation invoked by the
    > user will use the new asymmetric accessor functions instead of akcipher
    > api. The asymmetric subtype can internally use akcipher api or
    > invoke operations defined by a given subtype, depending on the
    > key type.
    >
    > Signed-off-by: Tadeusz Struk <tadeusz.struk@intel.com>
    > ---
    > crypto/af_alg.c | 10 ++
    > crypto/algif_akcipher.c | 207
    > ++++++++++++++++++++++++++++++++++++++++++- include/crypto/if_alg.h |
    > 1
    > include/uapi/linux/if_alg.h | 2
    > 4 files changed, 215 insertions(+), 5 deletions(-)
    >
    > diff --git a/crypto/af_alg.c b/crypto/af_alg.c
    > index 24dc082..59c8244 100644
    > --- a/crypto/af_alg.c
    > +++ b/crypto/af_alg.c
    > @@ -260,6 +260,16 @@ static int alg_setsockopt(struct socket *sock, int
    > level, int optname,
    >
    > err = alg_setkey(sk, optval, optlen, type->setpubkey);
    > break;
    > +
    > + case ALG_SET_KEY_ID:
    > + case ALG_SET_PUBKEY_ID:
    > + /* ALG_SET_KEY_ID is only for akcipher */
    > + if (!strcmp(type->name, "akcipher") ||
    > + sock->state == SS_CONNECTED)
    > + goto unlock;
    > +
    > + err = alg_setkey(sk, optval, optlen, type->setkeyid);
    > + break;
    > case ALG_SET_AEAD_AUTHSIZE:
    > if (sock->state == SS_CONNECTED)
    > goto unlock;
    > diff --git a/crypto/algif_akcipher.c b/crypto/algif_akcipher.c
    > index e00793d..f486b6d 100644
    > --- a/crypto/algif_akcipher.c
    > +++ b/crypto/algif_akcipher.c
    > @@ -14,6 +14,8 @@
    > #include <crypto/akcipher.h>
    > #include <crypto/scatterwalk.h>
    > #include <crypto/if_alg.h>
    > +#include <crypto/public_key.h>
    > +#include <keys/asymmetric-type.h>
    > #include <linux/init.h>
    > #include <linux/list.h>
    > #include <linux/kernel.h>
    > @@ -29,6 +31,7 @@ struct akcipher_sg_list {
    >
    > struct akcipher_tfm {
    > struct crypto_akcipher *akcipher;
    > + char keyid[12];
    > bool has_key;
    > };
    >
    > @@ -37,6 +40,7 @@ struct akcipher_ctx {
    > struct af_alg_sgl rsgl[ALG_MAX_PAGES];
    >
    > struct af_alg_completion completion;
    > + struct key *key;
    >
    > unsigned long used;
    >
    > @@ -322,6 +326,153 @@ unlock:
    > return err ? err : size;
    > }
    >
    > +static int asym_key_encrypt(const struct key *key, struct akcipher_request
    > *req) +{
    > + struct kernel_pkey_params params = {0};
    > + char *src = NULL, *dst = NULL, *in, *out;
    > + int ret;
    > +
    > + if (!sg_is_last(req->src)) {
    > + src = kmalloc(req->src_len, GFP_KERNEL);
    > + if (!src)
    > + return -ENOMEM;
    > + scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0);
    > + in = src;
    > + } else {
    > + in = sg_virt(req->src);
    > + }
    > + if (!sg_is_last(req->dst)) {
    > + dst = kmalloc(req->dst_len, GFP_KERNEL);
    > + if (!dst) {
    > + kfree(src);
    > + return -ENOMEM;
    > + }
    > + out = dst;
    > + } else {
    > + out = sg_virt(req->dst);
    > + }
    > + params.key = (struct key *)key;
    > + params.data_len = req->src_len;
    > + params.enc_len = req->dst_len;
    > + ret = encrypt_blob(&params, in, out);
    > + if (ret)
    > + goto free;
    > +
    > + if (dst)
    > + scatterwalk_map_and_copy(dst, req->dst, 0, req->dst_len, 1);
    > +free:
    > + kfree(src);
    > + kfree(dst);
    > + return ret;
    > +}
    > +
    > +static int asym_key_decrypt(const struct key *key, struct akcipher_request
    > *req) +{
    > + struct kernel_pkey_params params = {0};
    > + char *src = NULL, *dst = NULL, *in, *out;
    > + int ret;
    > +
    > + if (!sg_is_last(req->src)) {
    > + src = kmalloc(req->src_len, GFP_KERNEL);
    > + if (!src)
    > + return -ENOMEM;
    > + scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0);
    > + in = src;
    > + } else {
    > + in = sg_virt(req->src);
    > + }
    > + if (!sg_is_last(req->dst)) {
    > + dst = kmalloc(req->dst_len, GFP_KERNEL);
    > + if (!dst) {
    > + kfree(src);
    > + return -ENOMEM;
    > + }
    > + out = dst;
    > + } else {
    > + out = sg_virt(req->dst);
    > + }
    > + params.key = (struct key *)key;
    > + params.data_len = req->src_len;
    > + params.enc_len = req->dst_len;
    > + ret = decrypt_blob(&params, in, out);
    > + if (ret)
    > + goto free;
    > +
    > + if (dst)
    > + scatterwalk_map_and_copy(dst, req->dst, 0, req->dst_len, 1);
    > +free:
    > + kfree(src);
    > + kfree(dst);
    > + return ret;
    > +}
    > +
    > +static int asym_key_sign(const struct key *key, struct akcipher_request
    > *req) +{
    > + struct kernel_pkey_params params = {0};
    > + char *src = NULL, *dst = NULL, *in, *out;
    > + int ret;
    > +
    > + if (!sg_is_last(req->src)) {
    > + src = kmalloc(req->src_len, GFP_KERNEL);
    > + if (!src)
    > + return -ENOMEM;
    > + scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0);
    > + in = src;
    > + } else {
    > + in = sg_virt(req->src);
    > + }
    > + if (!sg_is_last(req->dst)) {
    > + dst = kmalloc(req->dst_len, GFP_KERNEL);
    > + if (!dst) {
    > + kfree(src);
    > + return -ENOMEM;
    > + }
    > + out = dst;
    > + } else {
    > + out = sg_virt(req->dst);
    > + }
    > + params.key = (struct key *)key;
    > + params.data_len = req->src_len;
    > + params.enc_len = req->dst_len;
    > + ret = create_signature(&params, in, out);
    > + if (ret)
    > + goto free;
    > +
    > + if (dst)
    > + scatterwalk_map_and_copy(dst, req->dst, 0, req->dst_len, 1);
    > +free:
    > + kfree(src);
    > + kfree(dst);
    > + return ret;
    > +}
    > +
    > +static int asym_key_verify(const struct key *key, struct akcipher_request
    > *req) +{
    > + struct public_key_signature sig;
    > + char *src = NULL, *in;
    > + int ret;
    > +
    > + if (!sg_is_last(req->src)) {
    > + src = kmalloc(req->src_len, GFP_KERNEL);
    > + if (!src)
    > + return -ENOMEM;
    > + scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0);
    > + in = src;
    > + } else {
    > + in = sg_virt(req->src);
    > + }
    > + sig.pkey_algo = "rsa";
    > + sig.encoding = "pkcs1";
    > + /* Need to find a way to pass the hash param */
    > + sig.hash_algo = "sha1";

    This comment shall not hold up any merging with the mainline tree.

    I am not yet fully up to speed on the keys framework. But commonly, the
    signature's hash type is identical to the hash used for the key. Is there a
    way to obtain the key's signature type from the key framework?

    > + sig.digest_size = 20;
    > + sig.s_size = req->src_len;
    > + sig.s = src;
    > + ret = verify_signature(key, NULL, &sig);
    > + kfree(src);
    > + return ret;
    > +}
    > +
    > static int akcipher_recvmsg(struct socket *sock, struct msghdr *msg,
    > size_t ignored, int flags)
    > {
    > @@ -377,16 +528,28 @@ static int akcipher_recvmsg(struct socket *sock,
    > struct msghdr *msg, usedpages);
    > switch (ctx->op) {
    > case ALG_OP_VERIFY:
    > - err = crypto_akcipher_verify(&ctx->req);
    > + if (ctx->key)
    > + err = asym_key_verify(ctx->key, &ctx->req);
    > + else
    > + err = crypto_akcipher_verify(&ctx->req);
    > break;
    > case ALG_OP_SIGN:
    > - err = crypto_akcipher_sign(&ctx->req);
    > + if (ctx->key)
    > + err = asym_key_sign(ctx->key, &ctx->req);
    > + else
    > + err = crypto_akcipher_sign(&ctx->req);
    > break;
    > case ALG_OP_ENCRYPT:
    > - err = crypto_akcipher_encrypt(&ctx->req);
    > + if (ctx->key)
    > + err = asym_key_encrypt(ctx->key, &ctx->req);
    > + else
    > + err = crypto_akcipher_encrypt(&ctx->req);
    > break;
    > case ALG_OP_DECRYPT:
    > - err = crypto_akcipher_decrypt(&ctx->req);
    > + if (ctx->key)
    > + err = asym_key_decrypt(ctx->key, &ctx->req);
    > + else
    > + err = crypto_akcipher_decrypt(&ctx->req);
    > break;
    > default:
    > err = -EFAULT;
    > @@ -579,6 +742,27 @@ static void akcipher_release(void *private)
    > kfree(tfm);
    > }
    >
    > +static int akcipher_setkeyid(void *private, const u8 *key, unsigned int
    > keylen) +{
    > + struct akcipher_tfm *tfm = private;
    > + struct key *akey;
    > + u32 keyid = *((u32 *)key);
    > + int err = -ENOKEY;
    > +
    > + /* Store the key id and verify that a key with the given id is
    present.
    > + * The actual key will be acquired in the accept_parent function
    > + */
    > + sprintf(tfm->keyid, "id:%08x", keyid);
    > + akey = request_key(&key_type_asymmetric, tfm->keyid, NULL);
    > + if (IS_ERR(key))
    > + goto out;
    > +
    > + tfm->has_key = true;
    > + key_put(akey);
    > +out:
    > + return err;
    > +}
    > +
    > static int akcipher_setprivkey(void *private, const u8 *key,
    > unsigned int keylen)
    > {
    > @@ -610,6 +794,8 @@ static void akcipher_sock_destruct(struct sock *sk)
    > akcipher_put_sgl(sk);
    > sock_kfree_s(sk, ctx, ctx->len);
    > af_alg_release_parent(sk);
    > + if (ctx->key)
    > + key_put(ctx->key);
    > }
    >
    > static int akcipher_accept_parent_nokey(void *private, struct sock *sk)
    > @@ -618,6 +804,7 @@ static int akcipher_accept_parent_nokey(void *private,
    > struct sock *sk) struct alg_sock *ask = alg_sk(sk);
    > struct akcipher_tfm *tfm = private;
    > struct crypto_akcipher *akcipher = tfm->akcipher;
    > + struct key *key;
    > unsigned int len = sizeof(*ctx) + crypto_akcipher_reqsize(akcipher);
    >
    > ctx = sock_kmalloc(sk, len, GFP_KERNEL);
    > @@ -634,11 +821,20 @@ static int akcipher_accept_parent_nokey(void *private,
    > struct sock *sk) af_alg_init_completion(&ctx->completion);
    > sg_init_table(ctx->tsgl.sg, ALG_MAX_PAGES);
    >
    > - ask->private = ctx;
    > + if (strlen(tfm->keyid)) {
    > + key = request_key(&key_type_asymmetric, tfm->keyid, NULL);
    > + if (IS_ERR(key)) {
    > + sock_kfree_s(sk, ctx, len);
    > + return -ENOKEY;
    > + }
    >
    > + ctx->key = key;
    > + memset(tfm->keyid, '\0', sizeof(tfm->keyid));
    > + }
    > akcipher_request_set_tfm(&ctx->req, akcipher);
    > akcipher_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
    > af_alg_complete, &ctx->completion);
    > + ask->private = ctx;
    >
    > sk->sk_destruct = akcipher_sock_destruct;
    >
    > @@ -660,6 +856,7 @@ static const struct af_alg_type algif_type_akcipher = {
    > .release = akcipher_release,
    > .setkey = akcipher_setprivkey,
    > .setpubkey = akcipher_setpubkey,
    > + .setkeyid = akcipher_setkeyid,
    > .accept = akcipher_accept_parent,
    > .accept_nokey = akcipher_accept_parent_nokey,
    > .ops = &algif_akcipher_ops,
    > diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h
    > index 6c3e6e7..09c99ab 100644
    > --- a/include/crypto/if_alg.h
    > +++ b/include/crypto/if_alg.h
    > @@ -53,6 +53,7 @@ struct af_alg_type {
    > void (*release)(void *private);
    > int (*setkey)(void *private, const u8 *key, unsigned int keylen);
    > int (*setpubkey)(void *private, const u8 *key, unsigned int keylen);
    > + int (*setkeyid)(void *private, const u8 *key, unsigned int keylen);
    > int (*accept)(void *private, struct sock *sk);
    > int (*accept_nokey)(void *private, struct sock *sk);
    > int (*setauthsize)(void *private, unsigned int authsize);
    > diff --git a/include/uapi/linux/if_alg.h b/include/uapi/linux/if_alg.h
    > index 02e6162..0379766 100644
    > --- a/include/uapi/linux/if_alg.h
    > +++ b/include/uapi/linux/if_alg.h
    > @@ -35,6 +35,8 @@ struct af_alg_iv {
    > #define ALG_SET_AEAD_ASSOCLEN 4
    > #define ALG_SET_AEAD_AUTHSIZE 5
    > #define ALG_SET_PUBKEY 6
    > +#define ALG_SET_PUBKEY_ID 7
    > +#define ALG_SET_KEY_ID 8
    >
    > /* Operations */
    > #define ALG_OP_DECRYPT 0


    Ciao
    Stephan

    \
     
     \ /
      Last update: 2016-05-06 17:01    [W:4.048 / U:0.088 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site