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@xxxxxxxxx> > --- > 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(¶ms, 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(¶ms, 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(¶ms, 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 -- To unsubscribe from this list: send the line "unsubscribe linux-crypto" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html