Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1395337 > unrolled thread
| Started by | Tadeusz Struk <tadeusz.struk@intel.com> |
|---|---|
| First post | 2016-05-05 22:00 +0200 |
| Last post | 2016-05-11 16:30 +0200 |
| Articles | 5 — 3 participants |
Back to article view | Back to linux.kernel
[PATCH RESEND v5 0/6] crypto: algif - add akcipher Tadeusz Struk <tadeusz.struk@intel.com> - 2016-05-05 22:00 +0200
[PATCH RESEND v5 6/6] crypto: AF_ALG - add support for key_id Tadeusz Struk <tadeusz.struk@intel.com> - 2016-05-05 22:00 +0200
Re: [PATCH RESEND v5 6/6] crypto: AF_ALG - add support for key_id Stephan Mueller <smueller@chronox.de> - 2016-05-06 17:00 +0200
[PATCH RESEND v5 5/6] crypto: algif_akcipher - add ops_nokey Tadeusz Struk <tadeusz.struk@intel.com> - 2016-05-05 22:00 +0200
Re: [PATCH RESEND v5 0/6] crypto: algif - add akcipher David Howells <dhowells@redhat.com> - 2016-05-11 16:30 +0200
| From | Tadeusz Struk <tadeusz.struk@intel.com> |
|---|---|
| Date | 2016-05-05 22:00 +0200 |
| Subject | [PATCH RESEND v5 0/6] crypto: algif - add akcipher |
| Message-ID | <rvxBf-6mz-3@gated-at.bofh.it> |
First four patches are a resend of the v3 algif_akcipher from
Stephan Mueller, with minor changes after rebase on top of 4.6-rc1.
The next three patches add support for keys stored in system
keyring subsystem.
First patch adds algif_akcipher nokey hadlers.
Second patch adds generic sign, verify, encrypt, decrypt accessors
functions to the asymmetric key type. These will be defined by
asymmetric subtypes, similarly to how public_key currently defines
the verify_signature function.
Third patch adds support for ALG_SET_KEY_ID and ALG_SET_PUBKEY_ID
commands to AF_ALG and setkeyid operation to the af_alg_type struct.
If the keyid is used then the afalg layer acquires the key for the
keyring subsystem and uses the new asymmetric accessor functions
instead of akcipher api. The asymmetric subtypes can use akcipher
api internally.
This is the same v5 version as before rebased on top of
http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=keys-asym-keyctl
v5 changes:
- drop public key changes and use new version provided by David
v4 changes:
- don't use internal public_key struct in af_alg.
- add generic accessor functions to asymmetric key type, which take
the generic struct key type and resolve the specific subtype internally
v3 changes:
- include Stephan's patches (rebased on 4.6-rc1)
- add algif_akcipher nokey hadlers
- add public_key info struct to public_key and helper query functions
- add a check if a key is a software accessible key on af_alg, and
return -ENOKEY if it isn't
v2 changes:
- pass the original skcipher request in ablkcipher.base.data instead of
casting it back from the ablkcipher request.
- rename _req to base_req
- dropped 3/3
---
Stephan Mueller (4):
crypto: AF_ALG -- add sign/verify API
crypto: AF_ALG -- add setpubkey setsockopt call
crypto: AF_ALG -- add asymmetric cipher interface
crypto: algif_akcipher - enable compilation
Tadeusz Struk (2):
crypto: algif_akcipher - add ops_nokey
crypto: AF_ALG - add support for key_id
crypto/Kconfig | 9
crypto/Makefile | 1
crypto/af_alg.c | 28 +
crypto/algif_akcipher.c | 884 +++++++++++++++++++++++++++++++++++++++++++
include/crypto/if_alg.h | 2
include/uapi/linux/if_alg.h | 5
6 files changed, 924 insertions(+), 5 deletions(-)
create mode 100644 crypto/algif_akcipher.c
--
TS
[toc] | [next] | [standalone]
| From | Tadeusz Struk <tadeusz.struk@intel.com> |
|---|---|
| Date | 2016-05-05 22:00 +0200 |
| Subject | [PATCH RESEND v5 6/6] crypto: AF_ALG - add support for key_id |
| Message-ID | <rvxBg-6mz-27@gated-at.bofh.it> |
| In reply to | #1395337 |
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(¶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";
+ 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
[toc] | [prev] | [next] | [standalone]
| From | Stephan Mueller <smueller@chronox.de> |
|---|---|
| Date | 2016-05-06 17:00 +0200 |
| Subject | Re: [PATCH RESEND v5 6/6] crypto: AF_ALG - add support for key_id |
| Message-ID | <rvPou-785-11@gated-at.bofh.it> |
| In reply to | #1395339 |
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(¶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
[toc] | [prev] | [next] | [standalone]
| From | Tadeusz Struk <tadeusz.struk@intel.com> |
|---|---|
| Date | 2016-05-05 22:00 +0200 |
| Subject | [PATCH RESEND v5 5/6] crypto: algif_akcipher - add ops_nokey |
| Message-ID | <rvxBg-6mz-35@gated-at.bofh.it> |
| In reply to | #1395337 |
Similar to algif_skcipher and algif_hash, algif_akcipher needs
to prevent user space from using the interface in an improper way.
This patch adds nokey ops handlers, which do just that.
Signed-off-by: Tadeusz Struk <tadeusz.struk@intel.com>
---
crypto/algif_akcipher.c | 159 +++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 152 insertions(+), 7 deletions(-)
diff --git a/crypto/algif_akcipher.c b/crypto/algif_akcipher.c
index 6342b6e..e00793d 100644
--- a/crypto/algif_akcipher.c
+++ b/crypto/algif_akcipher.c
@@ -27,6 +27,11 @@ struct akcipher_sg_list {
struct scatterlist sg[ALG_MAX_PAGES];
};
+struct akcipher_tfm {
+ struct crypto_akcipher *akcipher;
+ bool has_key;
+};
+
struct akcipher_ctx {
struct akcipher_sg_list tsgl;
struct af_alg_sgl rsgl[ALG_MAX_PAGES];
@@ -450,25 +455,151 @@ static struct proto_ops algif_akcipher_ops = {
.poll = akcipher_poll,
};
+static int akcipher_check_key(struct socket *sock)
+{
+ int err = 0;
+ 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;
+ 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 akcipher_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 = {
+ .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 = akcipher_poll,
+};
+
static void *akcipher_bind(const char *name, u32 type, u32 mask)
{
- return crypto_alloc_akcipher(name, type, 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;
+ return tfm;
}
static void akcipher_release(void *private)
{
- crypto_free_akcipher(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)
{
- return crypto_akcipher_set_priv_key(private, key, 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 err;
}
static int akcipher_setpubkey(void *private, const u8 *key, unsigned int keylen)
{
- return crypto_akcipher_set_pub_key(private, key, 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 err;
}
static void akcipher_sock_destruct(struct sock *sk)
@@ -481,11 +612,13 @@ static void akcipher_sock_destruct(struct sock *sk)
af_alg_release_parent(sk);
}
-static int akcipher_accept_parent(void *private, struct sock *sk)
+static int akcipher_accept_parent_nokey(void *private, struct sock *sk)
{
struct akcipher_ctx *ctx;
struct alg_sock *ask = alg_sk(sk);
- unsigned int len = sizeof(*ctx) + crypto_akcipher_reqsize(private);
+ struct akcipher_tfm *tfm = private;
+ struct crypto_akcipher *akcipher = tfm->akcipher;
+ unsigned int len = sizeof(*ctx) + crypto_akcipher_reqsize(akcipher);
ctx = sock_kmalloc(sk, len, GFP_KERNEL);
if (!ctx)
@@ -503,7 +636,7 @@ static int akcipher_accept_parent(void *private, struct sock *sk)
ask->private = ctx;
- akcipher_request_set_tfm(&ctx->req, private);
+ akcipher_request_set_tfm(&ctx->req, akcipher);
akcipher_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
af_alg_complete, &ctx->completion);
@@ -512,13 +645,25 @@ static int akcipher_accept_parent(void *private, struct sock *sk)
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,
.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
};
[toc] | [prev] | [next] | [standalone]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2016-05-11 16:30 +0200 |
| Message-ID | <rxDjc-1to-31@gated-at.bofh.it> |
| In reply to | #1395337 |
Tadeusz Struk <tadeusz.struk@intel.com> wrote: > This is the same v5 version as before rebased on top of > http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=keys-asym-keyctl I've just reposted this. The interface you're using should be the same, I think, but the details underneath have changed. Also, you can now supply private keys to the kernel if they're PKCS#8 encoded and keyctls are supplied that do encryption, decryption, signing and verifying, e.g.: j=`openssl pkcs8 -in ~/pkcs7/firmwarekey2.priv -topk8 -nocrypt -outform DER | \ keyctl padd asymmetric foo @s` echo -n abcdefghijklmnopqrst >/tmp/data keyctl pkey_encrypt $j 0 /tmp/data enc=pkcs1 >/tmp/enc keyctl pkey_decrypt $j 0 /tmp/enc enc=pkcs1 >/tmp/dec cmp /tmp/data /tmp/dec keyctl pkey_sign $j 0 /tmp/data enc=pkcs1 hash=sha1 >/tmp/sig keyctl pkey_verify $j 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1 David
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web