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.st...@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

Reply via email to