summaryrefslogtreecommitdiff
path: root/crypto
diff options
context:
space:
mode:
authorHuan.Feng <huan.feng@starfivetech.com>2022-01-13 13:11:09 +0300
committerHuan.Feng <huan.feng@starfivetech.com>2022-01-13 13:11:09 +0300
commit1d1ebfc3c5a005e765065c8f3424b46dac5a3b6d (patch)
treefca44b462d0f2f321b506b29e4b2418f2966020b /crypto
parent0efc2338f6b97a2fa6f3baaa4be040e39953e107 (diff)
downloadlinux-1d1ebfc3c5a005e765065c8f3424b46dac5a3b6d.tar.xz
add patches for libkcapi tool
Diffstat (limited to 'crypto')
-rw-r--r--crypto/Kconfig19
-rw-r--r--crypto/Makefile9
-rw-r--r--crypto/af_alg.c44
-rw-r--r--crypto/algif_aead.c31
-rw-r--r--crypto/algif_akcipher.c468
-rw-r--r--crypto/algif_kpp.c606
-rw-r--r--crypto/algif_skcipher.c25
-rw-r--r--crypto/dh.c38
-rw-r--r--crypto/dh_helper.c57
-rw-r--r--crypto/dhparameter.asn14
10 files changed, 1257 insertions, 44 deletions
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 536df4b6b825..c8bc811bf2b3 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -1916,6 +1916,25 @@ config CRYPTO_STATS
- encrypt/decrypt/sign/verify numbers for asymmetric operations
- generate/seed numbers for rng operations
+config CRYPTO_USER_API_AKCIPHER
+ tristate "User-space interface for asymmetric key cipher algorithms"
+ depends on NET
+ select CRYPTO_AKCIPHER2
+ select CRYPTO_USER_API
+ help
+ This option enables the user-space interface for asymmetric
+ key cipher algorithms.
+
+config CRYPTO_USER_API_KPP
+ tristate "User-space interface for key protocol primitives algorithms"
+ depends on NET
+ select CRYPTO_KPP2
+ select CRYPTO_USER_API
+ help
+ This option enables the user-spaces interface for key protocol
+ primitives algorithms. This covers Diffie-Hellman and EC
+ Diffie-Hellman.
+
config CRYPTO_HASH_INFO
bool
diff --git a/crypto/Makefile b/crypto/Makefile
index c633f15a0481..81401bb006c8 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -27,7 +27,12 @@ obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o
obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o
obj-$(CONFIG_CRYPTO_KPP2) += kpp.o
-dh_generic-y := dh.o
+$(obj)/dhparameter-asn1.o: $(obj)/dhparameter.asn1.c $(obj)/dhparameter.asn1.h
+$(obj)/dh_helper.o: $(obj)/dhparameter.asn1.h
+clean-files += dhparameter.asn1.c dhparameter.asn1.h
+
+dh_generic-y := dhparameter.asn1.o
+dh_generic-y += dh.o
dh_generic-y += dh_helper.o
obj-$(CONFIG_CRYPTO_DH) += dh_generic.o
@@ -171,6 +176,8 @@ obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
obj-$(CONFIG_CRYPTO_USER_API_RNG) += algif_rng.o
obj-$(CONFIG_CRYPTO_USER_API_AEAD) += algif_aead.o
+obj-$(CONFIG_CRYPTO_USER_API_AKCIPHER) += algif_akcipher.o
+obj-$(CONFIG_CRYPTO_USER_API_KPP) += algif_kpp.o
obj-$(CONFIG_CRYPTO_ZSTD) += zstd.o
obj-$(CONFIG_CRYPTO_OFB) += ofb.o
obj-$(CONFIG_CRYPTO_ECC) += ecc.o
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
index 8bd288d2b089..325892ac45c1 100644
--- a/crypto/af_alg.c
+++ b/crypto/af_alg.c
@@ -202,13 +202,17 @@ unlock:
return err;
}
-static int alg_setkey(struct sock *sk, sockptr_t ukey, unsigned int keylen)
+static int alg_setkey(struct sock *sk, sockptr_t ukey, unsigned int keylen,
+ int (*setkey)(void *private, const u8 *key,
+ unsigned int keylen))
{
struct alg_sock *ask = alg_sk(sk);
- const struct af_alg_type *type = ask->type;
u8 *key;
int err;
+ if (!setkey)
+ return -ENOPROTOOPT;
+
key = sock_kmalloc(sk, keylen, GFP_KERNEL);
if (!key)
return -ENOMEM;
@@ -217,8 +221,7 @@ static int alg_setkey(struct sock *sk, sockptr_t ukey, unsigned int keylen)
if (copy_from_sockptr(key, ukey, keylen))
goto out;
- err = type->setkey(ask->private, key, keylen);
-
+ err = setkey(ask->private, key, keylen);
out:
sock_kzfree_s(sk, key, keylen);
@@ -243,18 +246,23 @@ static int alg_setsockopt(struct socket *sock, int level, int optname,
if (level != SOL_ALG || !type)
goto unlock;
+ if (sock->state == SS_CONNECTED)
+ goto unlock;
+
switch (optname) {
case ALG_SET_KEY:
- if (sock->state == SS_CONNECTED)
- goto unlock;
- if (!type->setkey)
- goto unlock;
-
- err = alg_setkey(sk, optval, optlen);
+ err = alg_setkey(sk, optval, optlen, type->setkey);
+ break;
+ case ALG_SET_PUBKEY:
+ err = alg_setkey(sk, optval, optlen, type->setpubkey);
+ break;
+ case ALG_SET_DH_PARAMETERS:
+ err = alg_setkey(sk, optval, optlen, type->dhparams);
+ break;
+ case ALG_SET_ECDH_CURVE:
+ err = alg_setkey(sk, optval, optlen, type->ecdhcurve);
break;
case ALG_SET_AEAD_AUTHSIZE:
- if (sock->state == SS_CONNECTED)
- goto unlock;
if (!type->setauthsize)
goto unlock;
err = type->setauthsize(ask->private, optlen);
@@ -836,7 +844,7 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size,
struct af_alg_tsgl *sgl;
struct af_alg_control con = {};
long copied = 0;
- bool enc = false;
+ int op = 0;
bool init = false;
int err = 0;
@@ -847,11 +855,13 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size,
init = true;
switch (con.op) {
+ case ALG_OP_VERIFY:
+ case ALG_OP_SIGN:
case ALG_OP_ENCRYPT:
- enc = true;
- break;
case ALG_OP_DECRYPT:
- enc = false;
+ case ALG_OP_KEYGEN:
+ case ALG_OP_SSGEN:
+ op = con.op;
break;
default:
return -EINVAL;
@@ -875,7 +885,7 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size,
ctx->init = true;
if (init) {
- ctx->enc = enc;
+ ctx->op = op;
if (con.iv)
memcpy(ctx->iv, con.iv->iv, ivsize);
diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c
index 42493b4d8ce4..178eea69af0f 100644
--- a/crypto/algif_aead.c
+++ b/crypto/algif_aead.c
@@ -55,7 +55,7 @@ static inline bool aead_sufficient_data(struct sock *sk)
* The minimum amount of memory needed for an AEAD cipher is
* the AAD and in case of decryption the tag.
*/
- return ctx->used >= ctx->aead_assoclen + (ctx->enc ? 0 : as);
+ return ctx->used >= ctx->aead_assoclen + (ctx->op ? 0 : as);
}
static int aead_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
@@ -71,6 +71,19 @@ static int aead_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
return af_alg_sendmsg(sock, msg, size, ivsize);
}
+static inline int aead_cipher_op(struct af_alg_ctx *ctx,
+ struct af_alg_async_req *areq)
+{
+ switch (ctx->op) {
+ case ALG_OP_ENCRYPT:
+ return crypto_aead_encrypt(&areq->cra_u.aead_req);
+ case ALG_OP_DECRYPT:
+ return crypto_aead_decrypt(&areq->cra_u.aead_req);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static int crypto_aead_copy_sgl(struct crypto_sync_skcipher *null_tfm,
struct scatterlist *src,
struct scatterlist *dst, unsigned int len)
@@ -138,7 +151,7 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
* buffer provides the tag which is consumed resulting in only the
* plaintext without a buffer for the tag returned to the caller.
*/
- if (ctx->enc)
+ if (ctx->op)
outlen = used + as;
else
outlen = used - as;
@@ -212,7 +225,7 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
/* Use the RX SGL as source (and destination) for crypto op. */
rsgl_src = areq->first_rsgl.sgl.sg;
- if (ctx->enc) {
+ if (ctx->op == ALG_OP_ENCRYPT) {
/*
* Encryption operation - The in-place cipher operation is
* achieved by the following operation:
@@ -228,7 +241,7 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
if (err)
goto free;
af_alg_pull_tsgl(sk, processed, NULL, 0);
- } else {
+ } else if (ctx->op == ALG_OP_DECRYPT) {
/*
* Decryption operation - To achieve an in-place cipher
* operation, the following SGL structure is used:
@@ -293,9 +306,8 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
aead_request_set_callback(&areq->cra_u.aead_req,
CRYPTO_TFM_REQ_MAY_SLEEP,
af_alg_async_cb, areq);
- err = ctx->enc ? crypto_aead_encrypt(&areq->cra_u.aead_req) :
- crypto_aead_decrypt(&areq->cra_u.aead_req);
-
+ err = aead_cipher_op(ctx, areq);
+
/* AIO operation in progress */
if (err == -EINPROGRESS)
return -EIOCBQUEUED;
@@ -307,10 +319,7 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
CRYPTO_TFM_REQ_MAY_SLEEP |
CRYPTO_TFM_REQ_MAY_BACKLOG,
crypto_req_done, &ctx->wait);
- err = crypto_wait_req(ctx->enc ?
- crypto_aead_encrypt(&areq->cra_u.aead_req) :
- crypto_aead_decrypt(&areq->cra_u.aead_req),
- &ctx->wait);
+ err = crypto_wait_req(aead_cipher_op(ctx, areq), &ctx->wait);
}
diff --git a/crypto/algif_akcipher.c b/crypto/algif_akcipher.c
new file mode 100644
index 000000000000..a9fb618458ce
--- /dev/null
+++ b/crypto/algif_akcipher.c
@@ -0,0 +1,468 @@
+/*
+ * algif_akcipher: User-space interface for asymmetric cipher algorithms
+ *
+ * Copyright (C) 2018 - 2020, 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/if_alg.h>
+#include <crypto/scatterwalk.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>
+
+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 inline int akcipher_cipher_op(struct af_alg_ctx *ctx,
+ struct af_alg_async_req *areq)
+{
+ switch (ctx->op) {
+ case ALG_OP_ENCRYPT:
+ return crypto_akcipher_encrypt(&areq->cra_u.akcipher_req);
+ case ALG_OP_DECRYPT:
+ return crypto_akcipher_decrypt(&areq->cra_u.akcipher_req);
+ case ALG_OP_SIGN:
+ return crypto_akcipher_sign(&areq->cra_u.akcipher_req);
+ case ALG_OP_VERIFY:
+ return crypto_akcipher_verify(&areq->cra_u.akcipher_req);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+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;
+ size_t len;
+ size_t used;
+ int err;
+ int maxsize;
+
+ if (!ctx->used) {
+ err = af_alg_wait_for_data(sk, flags, 0);
+ if (err)
+ return err;
+ }
+
+ 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 */
+ sock_hold(sk);
+ areq->iocb = msg->msg_iocb;
+
+ /* Remember output size that will be generated. */
+ areq->outlen = areq->cra_u.akcipher_req.dst_len ?
+ areq->cra_u.akcipher_req.dst_len : len;
+
+ akcipher_request_set_callback(&areq->cra_u.akcipher_req,
+ CRYPTO_TFM_REQ_MAY_SLEEP,
+ af_alg_async_cb, areq);
+ err = akcipher_cipher_op(ctx, areq);
+
+ /* AIO operation in progress */
+ if (err == -EINPROGRESS || err == -EBUSY)
+ return -EIOCBQUEUED;
+
+ sock_put(sk);
+ } else {
+ /* Synchronous operation */
+ akcipher_request_set_callback(&areq->cra_u.akcipher_req,
+ CRYPTO_TFM_REQ_MAY_SLEEP |
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done,
+ &ctx->wait);
+ err = crypto_wait_req(akcipher_cipher_op(ctx, areq),
+ &ctx->wait);
+ }
+
+free:
+ af_alg_free_resources(areq);
+
+ 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;
+ int ret = 0;
+ int err;
+
+ lock_sock(sk);
+
+ while (msg_data_left(msg)) {
+ err = _akcipher_recvmsg(sock, msg, ignored, flags);
+
+ /*
+ * 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) {
+ 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 = {
+ .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,
+ .mmap = sock_no_mmap,
+ .bind = sock_no_bind,
+ .accept = sock_no_accept,
+
+ .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)
+{
+ struct sock *psk;
+ struct alg_sock *pask;
+ struct akcipher_tfm *tfm;
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ int err = 0;
+
+ lock_sock(sk);
+ if (!atomic_read(&ask->nokey_refcnt))
+ goto unlock_child;
+
+ psk = ask->parent;
+ pask = alg_sk(ask->parent);
+ tfm = pask->private;
+
+ lock_sock_nested(psk, SINGLE_DEPTH_NESTING);
+ if (!tfm->has_key) {
+ err = -ENOKEY;
+ goto unlock;
+ }
+
+ atomic_dec(&pask->nokey_refcnt);
+ atomic_set(&ask->nokey_refcnt, 0);
+
+ 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 = {
+ .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,
+ .mmap = sock_no_mmap,
+ .bind = sock_no_bind,
+ .accept = sock_no_accept,
+
+ .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 = kmalloc(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->has_key = false;
+
+ 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);
+
+ 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;
+
+ INIT_LIST_HEAD(&ctx->tsgl_list);
+ ctx->len = len;
+ ctx->used = 0;
+ atomic_set(&ctx->rcvused, 0);
+ ctx->more = 0;
+ ctx->merge = 0;
+ ctx->op = 0;
+ crypto_init_wait(&ctx->wait);
+
+ 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);
+
+ 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/crypto/algif_kpp.c b/crypto/algif_kpp.c
new file mode 100644
index 000000000000..03fa7ba382b0
--- /dev/null
+++ b/crypto/algif_kpp.c
@@ -0,0 +1,606 @@
+/*
+ * algif_kpp: User-space interface for key protocol primitives algorithms
+ *
+ * Copyright (C) 2018 - 2020, Stephan Mueller <smueller@chronox.de>
+ *
+ * This file provides the user-space API for key protocol primitives.
+ *
+ * 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/dh.h>
+#include <crypto/ecdh.h>
+#include <crypto/kpp.h>
+#include <crypto/rng.h>
+#include <crypto/if_alg.h>
+#include <crypto/scatterwalk.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <net/sock.h>
+
+struct kpp_tfm {
+ struct crypto_kpp *kpp;
+ bool has_key;
+
+#define KPP_NO_PARAMS 0
+#define KPP_DH_PARAMS 1
+#define KPP_ECDH_PARAMS 2
+ int has_params; /* Type of KPP mechanism */
+};
+
+static int kpp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+{
+ return af_alg_sendmsg(sock, msg, size, 0);
+}
+
+static inline int kpp_cipher_op(struct af_alg_ctx *ctx,
+ struct af_alg_async_req *areq)
+{
+ switch (ctx->op) {
+ case ALG_OP_KEYGEN:
+ return crypto_kpp_generate_public_key(&areq->cra_u.kpp_req);
+ case ALG_OP_SSGEN:
+ return crypto_kpp_compute_shared_secret(&areq->cra_u.kpp_req);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int _kpp_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 kpp_tfm *kpp = pask->private;
+ struct crypto_kpp *tfm = kpp->kpp;
+ struct af_alg_async_req *areq;
+ size_t len;
+ size_t used = 0;
+ int err;
+ int maxsize;
+
+ if (!ctx->used) {
+ err = af_alg_wait_for_data(sk, flags, 0);
+ if (err)
+ return err;
+ }
+
+ maxsize = crypto_kpp_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_kpp_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.
+ */
+ if (ctx->op == ALG_OP_SSGEN) {
+ 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 */
+ kpp_request_set_input(&areq->cra_u.kpp_req, areq->tsgl, used);
+ kpp_request_set_output(&areq->cra_u.kpp_req, areq->first_rsgl.sgl.sg,
+ len);
+ kpp_request_set_tfm(&areq->cra_u.kpp_req, tfm);
+
+ if (msg->msg_iocb && !is_sync_kiocb(msg->msg_iocb)) {
+ /* AIO operation */
+ sock_hold(sk);
+ areq->iocb = msg->msg_iocb;
+
+ /* Remember output size that will be generated. */
+ areq->outlen = len;
+
+ kpp_request_set_callback(&areq->cra_u.kpp_req,
+ CRYPTO_TFM_REQ_MAY_SLEEP,
+ af_alg_async_cb, areq);
+ err = kpp_cipher_op(ctx, areq);
+
+ /* AIO operation in progress */
+ if (err == -EINPROGRESS || err == -EBUSY)
+ return -EIOCBQUEUED;
+
+ sock_put(sk);
+ } else {
+ /* Synchronous operation */
+ kpp_request_set_callback(&areq->cra_u.kpp_req,
+ CRYPTO_TFM_REQ_MAY_SLEEP |
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done,
+ &ctx->wait);
+ err = crypto_wait_req(kpp_cipher_op(ctx, areq), &ctx->wait);
+ }
+
+free:
+ af_alg_free_resources(areq);
+
+ return err ? err : len;
+}
+
+static int kpp_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 kpp_tfm *kpp = pask->private;
+ struct crypto_kpp *tfm = kpp->kpp;
+ int ret = 0;
+ int err;
+
+ lock_sock(sk);
+
+ while (msg_data_left(msg)) {
+ err = _kpp_recvmsg(sock, msg, ignored, flags);
+
+ /*
+ * 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) {
+ if (err == -EIOCBQUEUED || err == -EBADMSG || !ret)
+ ret = err;
+ goto out;
+ }
+
+ ret += err;
+
+ /*
+ * The caller must provide crypto_kpp_maxsize per request.
+ * If he provides more, we conclude that multiple kpp
+ * operations are requested.
+ */
+ iov_iter_advance(&msg->msg_iter,
+ crypto_kpp_maxsize(tfm) - err);
+ }
+
+out:
+
+ af_alg_wmem_wakeup(sk);
+ release_sock(sk);
+ return ret;
+}
+
+static struct proto_ops algif_kpp_ops = {
+ .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,
+ .mmap = sock_no_mmap,
+ .bind = sock_no_bind,
+ .accept = sock_no_accept,
+
+ .release = af_alg_release,
+ .sendmsg = kpp_sendmsg,
+ .sendpage = af_alg_sendpage,
+ .recvmsg = kpp_recvmsg,
+ .poll = af_alg_poll,
+};
+
+static int kpp_check_key(struct socket *sock)
+{
+ struct sock *psk;
+ struct alg_sock *pask;
+ struct kpp_tfm *tfm;
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ int err = 0;
+
+ lock_sock(sk);
+ if (!atomic_read(&ask->refcnt))
+ goto unlock_child;
+
+ psk = ask->parent;
+ pask = alg_sk(ask->parent);
+ tfm = pask->private;
+
+ lock_sock_nested(psk, SINGLE_DEPTH_NESTING);
+ if (!tfm->has_key || (tfm->has_params == KPP_NO_PARAMS)) {
+ err = -ENOKEY;
+ goto unlock;
+ }
+
+ atomic_dec(&pask->refcnt);
+ atomic_set(&ask->refcnt, 0);
+
+ err = 0;
+
+unlock:
+ release_sock(psk);
+unlock_child:
+ release_sock(sk);
+
+ return err;
+}
+
+static int kpp_sendmsg_nokey(struct socket *sock, struct msghdr *msg,
+ size_t size)
+{
+ int err;
+
+ err = kpp_check_key(sock);
+ if (err)
+ return err;
+
+ return kpp_sendmsg(sock, msg, size);
+}
+
+static ssize_t kpp_sendpage_nokey(struct socket *sock, struct page *page,
+ int offset, size_t size, int flags)
+{
+ int err;
+
+ err = kpp_check_key(sock);
+ if (err)
+ return err;
+
+ return af_alg_sendpage(sock, page, offset, size, flags);
+}
+
+static int kpp_recvmsg_nokey(struct socket *sock, struct msghdr *msg,
+ size_t ignored, int flags)
+{
+ int err;
+
+ err = kpp_check_key(sock);
+ if (err)
+ return err;
+
+ return kpp_recvmsg(sock, msg, ignored, flags);
+}
+
+static struct proto_ops algif_kpp_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,
+ .mmap = sock_no_mmap,
+ .bind = sock_no_bind,
+ .accept = sock_no_accept,
+
+ .release = af_alg_release,
+ .sendmsg = kpp_sendmsg_nokey,
+ .sendpage = kpp_sendpage_nokey,
+ .recvmsg = kpp_recvmsg_nokey,
+ .poll = af_alg_poll,
+};
+
+static void *kpp_bind(const char *name, u32 type, u32 mask)
+{
+ struct kpp_tfm *tfm;
+ struct crypto_kpp *kpp;
+
+ tfm = kmalloc(sizeof(*tfm), GFP_KERNEL);
+ if (!tfm)
+ return ERR_PTR(-ENOMEM);
+
+ kpp = crypto_alloc_kpp(name, type, mask);
+ if (IS_ERR(kpp)) {
+ kfree(tfm);
+ return ERR_CAST(kpp);
+ }
+
+ tfm->kpp = kpp;
+ tfm->has_key = false;
+ tfm->has_params = KPP_NO_PARAMS;
+
+ return tfm;
+}
+
+static void kpp_release(void *private)
+{
+ struct kpp_tfm *tfm = private;
+ struct crypto_kpp *kpp = tfm->kpp;
+
+ crypto_free_kpp(kpp);
+ kfree(tfm);
+}
+
+static int kpp_dh_set_secret(struct crypto_kpp *tfm, struct dh *params)
+{
+ char *packed_key = NULL;
+ unsigned int packed_key_len;
+ int ret;
+
+ packed_key_len = crypto_dh_key_len(params);
+ packed_key = kmalloc(packed_key_len, GFP_KERNEL);
+ if (!packed_key)
+ return -ENOMEM;
+
+ ret = crypto_dh_encode_key(packed_key, packed_key_len, params);
+ if (ret)
+ goto out;
+
+ ret = crypto_kpp_set_secret(tfm, packed_key, packed_key_len);
+
+out:
+ kfree(packed_key);
+ return ret;
+}
+
+static int kpp_dh_set_privkey(struct crypto_kpp *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct dh params = {
+ .key = key,
+ .key_size = keylen,
+ .p = NULL,
+ .p_size = 0,
+ .g = NULL,
+ .g_size = 0,
+ };
+
+ return kpp_dh_set_secret(tfm, &params);
+}
+
+static int kpp_ecdh_set_secret(struct crypto_kpp *tfm, struct ecdh *params)
+{
+ char *packed_key = NULL;
+ unsigned int packed_key_len;
+ int ret;
+
+ packed_key_len = crypto_ecdh_key_len(params);
+ packed_key = kmalloc(packed_key_len, GFP_KERNEL);
+ if (!packed_key)
+ return -ENOMEM;
+
+ ret = crypto_ecdh_encode_key(packed_key, packed_key_len, params);
+ if (ret)
+ goto out;
+
+ ret = crypto_kpp_set_secret(tfm, packed_key, packed_key_len);
+
+out:
+ kfree(packed_key);
+ return ret;
+}
+
+static int kpp_ecdh_set_privkey(struct crypto_kpp *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct ecdh params = {
+ .curve_id = 0,
+ .key = key,
+ .key_size = keylen,
+ };
+
+ return kpp_ecdh_set_secret(tfm, &params);
+}
+
+static int kpp_setprivkey(void *private, const u8 *key, unsigned int keylen)
+{
+ struct kpp_tfm *kpp = private;
+ struct crypto_kpp *tfm = kpp->kpp;
+ int err;
+
+ if (kpp->has_params == KPP_NO_PARAMS)
+ return -ENOKEY;
+
+ /* The DH code cannot generate private keys. ECDH can do that */
+ if ((!key || !keylen) && (kpp->has_params == KPP_DH_PARAMS)) {
+ kpp->has_key = false;
+ return -EOPNOTSUPP;
+ }
+
+ switch (kpp->has_params) {
+ case KPP_DH_PARAMS:
+ err = kpp_dh_set_privkey(tfm, key, keylen);
+ break;
+ case KPP_ECDH_PARAMS:
+ err = kpp_ecdh_set_privkey(tfm, key, keylen);
+ break;
+ default:
+ err = -EFAULT;
+ }
+
+ kpp->has_key = !err;
+
+ /* Return the maximum size of the kpp operation. */
+ if (!err)
+ err = crypto_kpp_maxsize(tfm);
+
+ return err;
+}
+
+static int kpp_dh_setparams_pkcs3(void *private, const u8 *params,
+ unsigned int paramslen)
+{
+ struct kpp_tfm *kpp = private;
+ struct crypto_kpp *tfm = kpp->kpp;
+ int err;
+
+ /* If parameters were already set, disallow setting them again. */
+ if (kpp->has_params != KPP_NO_PARAMS)
+ return -EINVAL;
+
+ err = crypto_kpp_set_params(tfm, params, paramslen);
+ if (!err) {
+ kpp->has_params = KPP_DH_PARAMS;
+ /* Return the maximum size of the kpp operation. */
+ err = crypto_kpp_maxsize(tfm);
+ } else
+ kpp->has_params = KPP_NO_PARAMS;
+
+ return err;
+}
+
+static int kpp_ecdh_setcurve(void *private, const u8 *curveid,
+ unsigned int curveidlen)
+{
+ struct kpp_tfm *kpp = private;
+ struct crypto_kpp *tfm = kpp->kpp;
+ int err;
+ struct ecdh params = {
+ .key = NULL,
+ .key_size = 0,
+ };
+
+ /* If parameters were already set, disallow setting them again. */
+ if (kpp->has_params != KPP_NO_PARAMS)
+ return -EINVAL;
+
+ if (curveidlen != sizeof(unsigned long))
+ return -EINVAL;
+
+ err = kstrtou16(curveid, 10, &params.curve_id);
+ if (err)
+ return err;
+
+ err = kpp_ecdh_set_secret(tfm, &params);
+ if (!err) {
+ kpp->has_params = KPP_ECDH_PARAMS;
+ /* Return the maximum size of the kpp operation. */
+ err = crypto_kpp_maxsize(tfm);
+ } else
+ kpp->has_params = KPP_NO_PARAMS;
+
+ return err;
+}
+
+static void kpp_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 kpp_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;
+
+ INIT_LIST_HEAD(&ctx->tsgl_list);
+ ctx->len = len;
+ ctx->used = 0;
+ atomic_set(&ctx->rcvused, 0);
+ ctx->more = 0;
+ ctx->merge = 0;
+ ctx->op = 0;
+ crypto_init_wait(&ctx->wait);
+
+ ask->private = ctx;
+
+ sk->sk_destruct = kpp_sock_destruct;
+
+ return 0;
+}
+
+static int kpp_accept_parent(void *private, struct sock *sk)
+{
+ struct kpp_tfm *tfm = private;
+
+ if (!tfm->has_key || (tfm->has_params == KPP_NO_PARAMS))
+ return -ENOKEY;
+
+ return kpp_accept_parent_nokey(private, sk);
+}
+
+static const struct af_alg_type algif_type_kpp = {
+ .bind = kpp_bind,
+ .release = kpp_release,
+ .setkey = kpp_setprivkey,
+ .setpubkey = NULL,
+ .dhparams = kpp_dh_setparams_pkcs3,
+ .ecdhcurve = kpp_ecdh_setcurve,
+ .setauthsize = NULL,
+ .accept = kpp_accept_parent,
+ .accept_nokey = kpp_accept_parent_nokey,
+ .ops = &algif_kpp_ops,
+ .ops_nokey = &algif_kpp_ops_nokey,
+ .name = "kpp",
+ .owner = THIS_MODULE
+};
+
+static int __init algif_kpp_init(void)
+{
+ return af_alg_register_type(&algif_type_kpp);
+}
+
+static void __exit algif_kpp_exit(void)
+{
+ int err = af_alg_unregister_type(&algif_type_kpp);
+
+ BUG_ON(err);
+}
+
+module_init(algif_kpp_init);
+module_exit(algif_kpp_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
+MODULE_DESCRIPTION("Key protocol primitives kernel crypto API user space interface");
+
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index ee8890ee8f33..952a8e698b5c 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -47,6 +47,19 @@ static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg,
return af_alg_sendmsg(sock, msg, size, ivsize);
}
+static inline int skcipher_cipher_op(struct af_alg_ctx *ctx,
+ struct af_alg_async_req *areq)
+{
+ switch (ctx->op) {
+ case ALG_OP_ENCRYPT:
+ return crypto_skcipher_encrypt(&areq->cra_u.skcipher_req);
+ case ALG_OP_DECRYPT:
+ return crypto_skcipher_decrypt(&areq->cra_u.skcipher_req);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg,
size_t ignored, int flags)
{
@@ -118,10 +131,8 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg,
skcipher_request_set_callback(&areq->cra_u.skcipher_req,
CRYPTO_TFM_REQ_MAY_SLEEP,
af_alg_async_cb, areq);
- err = ctx->enc ?
- crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) :
- crypto_skcipher_decrypt(&areq->cra_u.skcipher_req);
-
+ err = skcipher_cipher_op(ctx, areq);
+
/* AIO operation in progress */
if (err == -EINPROGRESS)
return -EIOCBQUEUED;
@@ -133,10 +144,8 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg,
CRYPTO_TFM_REQ_MAY_SLEEP |
CRYPTO_TFM_REQ_MAY_BACKLOG,
crypto_req_done, &ctx->wait);
- err = crypto_wait_req(ctx->enc ?
- crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) :
- crypto_skcipher_decrypt(&areq->cra_u.skcipher_req),
- &ctx->wait);
+ err = crypto_wait_req(skcipher_cipher_op(ctx, areq),
+ &ctx->wait);
}
diff --git a/crypto/dh.c b/crypto/dh.c
index cd4f32092e5c..654cbe77586e 100644
--- a/crypto/dh.c
+++ b/crypto/dh.c
@@ -19,15 +19,25 @@ struct dh_ctx {
MPI xa; /* Value is guaranteed to be set. */
};
-static void dh_clear_ctx(struct dh_ctx *ctx)
+static inline void dh_clear_params(struct dh_ctx *ctx)
{
mpi_free(ctx->p);
mpi_free(ctx->q);
mpi_free(ctx->g);
+}
+
+static inline void dh_clear_key(struct dh_ctx *ctx)
+{
mpi_free(ctx->xa);
memset(ctx, 0, sizeof(*ctx));
}
+static void dh_clear_ctx(struct dh_ctx *ctx)
+{
+ dh_clear_params(ctx);
+ dh_clear_key(ctx);
+}
+
/*
* If base is g we compute the public key
* ya = g^xa mod p; [RFC2631 sec 2.1.1]
@@ -52,6 +62,10 @@ static int dh_check_params_length(unsigned int p_len)
static int dh_set_params(struct dh_ctx *ctx, struct dh *params)
{
+ /* If DH parameters are not given, do not check them. */
+ if (!params->p_size && !params->g_size)
+ return 0;
+
if (dh_check_params_length(params->p_size << 3))
return -EINVAL;
@@ -72,6 +86,23 @@ static int dh_set_params(struct dh_ctx *ctx, struct dh *params)
return 0;
}
+static int dh_set_params_pkcs3(struct crypto_kpp *tfm, const void *param,
+ unsigned int param_len)
+{
+ struct dh_ctx *ctx = dh_get_ctx(tfm);
+ struct dh parsed_params;
+ int ret;
+
+ /* Free the old parameter if any */
+ dh_clear_params(ctx);
+
+ ret = dh_parse_params_pkcs3(&parsed_params, param, param_len);
+ if (ret)
+ return ret;
+
+ return dh_set_params(ctx, &parsed_params);
+}
+
static int dh_set_secret(struct crypto_kpp *tfm, const void *buf,
unsigned int len)
{
@@ -79,7 +110,7 @@ static int dh_set_secret(struct crypto_kpp *tfm, const void *buf,
struct dh params;
/* Free the old MPI key if any */
- dh_clear_ctx(ctx);
+ dh_clear_key(ctx);
if (crypto_dh_decode_key(buf, len, &params) < 0)
goto err_clear_ctx;
@@ -158,7 +189,7 @@ static int dh_compute_value(struct kpp_request *req)
if (!val)
return -ENOMEM;
- if (unlikely(!ctx->xa)) {
+ if (unlikely(!ctx->xa || !ctx->p || !ctx->g)) {
ret = -EINVAL;
goto err_free_val;
}
@@ -246,6 +277,7 @@ static void dh_exit_tfm(struct crypto_kpp *tfm)
}
static struct kpp_alg dh = {
+ .set_params = dh_set_params_pkcs3,
.set_secret = dh_set_secret,
.generate_public_key = dh_compute_value,
.compute_shared_secret = dh_compute_value,
diff --git a/crypto/dh_helper.c b/crypto/dh_helper.c
index 9fd5a42eea15..68986d09c4e6 100644
--- a/crypto/dh_helper.c
+++ b/crypto/dh_helper.c
@@ -9,6 +9,7 @@
#include <linux/string.h>
#include <crypto/dh.h>
#include <crypto/kpp.h>
+#include "dhparameter.asn1.h"
#define DH_KPP_SECRET_MIN_SIZE (sizeof(struct kpp_secret) + 4 * sizeof(int))
@@ -49,16 +50,26 @@ int crypto_dh_encode_key(char *buf, unsigned int len, const struct dh *params)
if (unlikely(!len))
return -EINVAL;
+ /* Prevention of out-of-bounds access in decode code path */
+ if ((!params->key && params->key_size) ||
+ (!params->p && params->p_size) ||
+ (!params->g && params->g_size))
+ return -EINVAL;
+
ptr = dh_pack_data(ptr, end, &secret, sizeof(secret));
ptr = dh_pack_data(ptr, end, &params->key_size,
sizeof(params->key_size));
ptr = dh_pack_data(ptr, end, &params->p_size, sizeof(params->p_size));
ptr = dh_pack_data(ptr, end, &params->q_size, sizeof(params->q_size));
ptr = dh_pack_data(ptr, end, &params->g_size, sizeof(params->g_size));
- ptr = dh_pack_data(ptr, end, params->key, params->key_size);
- ptr = dh_pack_data(ptr, end, params->p, params->p_size);
- ptr = dh_pack_data(ptr, end, params->q, params->q_size);
- ptr = dh_pack_data(ptr, end, params->g, params->g_size);
+ if (params->key)
+ ptr = dh_pack_data(ptr, end, params->key, params->key_size);
+ if (params->p)
+ ptr = dh_pack_data(ptr, end, params->p, params->p_size);
+ if (params->q)
+ ptr = dh_pack_data(ptr, end, params->q, params->q_size);
+ if (params->g)
+ ptr = dh_pack_data(ptr, end, params->g, params->g_size);
if (ptr != end)
return -EINVAL;
return 0;
@@ -116,3 +127,41 @@ int crypto_dh_decode_key(const char *buf, unsigned int len, struct dh *params)
return 0;
}
EXPORT_SYMBOL_GPL(crypto_dh_decode_key);
+
+int dh_get_p(void *context, size_t hdrlen, unsigned char tag, const void *value,
+ size_t vlen)
+{
+ struct dh *dh = context;
+
+ /* invalid key provided */
+ if (!value || !vlen)
+ return -EINVAL;
+
+ dh->p = value;
+ dh->p_size = vlen;
+
+ return 0;
+}
+
+int dh_get_g(void *context, size_t hdrlen, unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct dh *dh = context;
+
+ /* invalid base provided */
+ if (!value || !dh->p_size || !vlen || vlen > dh->p_size)
+ return -EINVAL;
+
+ dh->g = value;
+ dh->g_size = vlen;
+
+ return 0;
+}
+
+int dh_parse_params_pkcs3(struct dh *dh, const void *param,
+ unsigned int param_len)
+{
+ return asn1_ber_decoder(&dhparameter_decoder, dh, param, param_len);
+}
+EXPORT_SYMBOL_GPL(dh_parse_params_pkcs3);
+
diff --git a/crypto/dhparameter.asn1 b/crypto/dhparameter.asn1
new file mode 100644
index 000000000000..e9107c80478d
--- /dev/null
+++ b/crypto/dhparameter.asn1
@@ -0,0 +1,4 @@
+DHParameter ::= SEQUENCE {
+ prime INTEGER ({ dh_get_p }),
+ base INTEGER ({ dh_get_g })
+}