diff mbox series

KEYS: Add ECDH support

Message ID 20240330065506.3146-1-zhangyiqun@phytium.com.cn (mailing list archive)
State New
Headers show
Series KEYS: Add ECDH support | expand

Commit Message

Zhang Yiqun March 30, 2024, 6:55 a.m. UTC
This patch is to introduce ECDH into keyctl syscall for
userspace usage, containing public key generation and
shared secret computation.

It is mainly based on dh code, so it has the same condition
to the input which only user keys is supported. The output
result is storing into the buffer with the provided length.

Signed-off-by: Zhang Yiqun <zhangyiqun@phytium.com.cn>
---
 Documentation/security/keys/core.rst |  62 ++++++
 include/linux/compat.h               |   4 +
 include/uapi/linux/keyctl.h          |  11 +
 security/keys/Kconfig                |  12 +
 security/keys/Makefile               |   2 +
 security/keys/compat_ecdh.c          |  50 +++++
 security/keys/ecdh.c                 | 318 +++++++++++++++++++++++++++
 security/keys/internal.h             |  44 ++++
 security/keys/keyctl.c               |  10 +
 9 files changed, 513 insertions(+)
 create mode 100644 security/keys/compat_ecdh.c
 create mode 100644 security/keys/ecdh.c

Comments

Eric Biggers March 30, 2024, 7:04 a.m. UTC | #1
[+Cc linux-crypto]

On Sat, Mar 30, 2024 at 02:55:06PM +0800, Zhang Yiqun wrote:
> This patch is to introduce ECDH into keyctl syscall for
> userspace usage, containing public key generation and
> shared secret computation.
> 
> It is mainly based on dh code, so it has the same condition
> to the input which only user keys is supported. The output
> result is storing into the buffer with the provided length.
> 
> Signed-off-by: Zhang Yiqun <zhangyiqun@phytium.com.cn>
> ---
>  Documentation/security/keys/core.rst |  62 ++++++
>  include/linux/compat.h               |   4 +
>  include/uapi/linux/keyctl.h          |  11 +
>  security/keys/Kconfig                |  12 +
>  security/keys/Makefile               |   2 +
>  security/keys/compat_ecdh.c          |  50 +++++
>  security/keys/ecdh.c                 | 318 +++++++++++++++++++++++++++
>  security/keys/internal.h             |  44 ++++
>  security/keys/keyctl.c               |  10 +
>  9 files changed, 513 insertions(+)
>  create mode 100644 security/keys/compat_ecdh.c
>  create mode 100644 security/keys/ecdh.c

Nacked-by: Eric Biggers <ebiggers@google.com>

The existing KEYCTL_PKEY_*, KEYCTL_DH_COMPUTE, and AF_ALG are causing enough
problems.  We do not need any more UAPIs like this.  They are hard to maintain,
break often, not properly documented, increase the kernel's attack surface, and
what they do is better done in userspace.

Please refer to the recent thread
https://lore.kernel.org/linux-crypto/CZSHRUIJ4RKL.34T4EASV5DNJM@matfyz.cz/T/#u
where these issues were discussed in detail.

- Eric
Jarkko Sakkinen March 30, 2024, 11 a.m. UTC | #2
On Sat Mar 30, 2024 at 8:55 AM EET, Zhang Yiqun wrote:
> This patch is to introduce ECDH into keyctl syscall for

"Introduce Elliptic Curve Diffie-Hellman (ECDH)"

What does it mean to "introduce ECDH into keyctl syscall"?

> userspace usage, containing public key generation and
> shared secret computation.

Who else uses syscalls other than user space? You are implying
that there some other client.

>
> It is mainly based on dh code, so it has the same condition
> to the input which only user keys is supported. The output
> result is storing into the buffer with the provided length.

What is "dh code"?

>
> Signed-off-by: Zhang Yiqun <zhangyiqun@phytium.com.cn>
> ---
>  Documentation/security/keys/core.rst |  62 ++++++
>  include/linux/compat.h               |   4 +
>  include/uapi/linux/keyctl.h          |  11 +
>  security/keys/Kconfig                |  12 +
>  security/keys/Makefile               |   2 +
>  security/keys/compat_ecdh.c          |  50 +++++
>  security/keys/ecdh.c                 | 318 +++++++++++++++++++++++++++
>  security/keys/internal.h             |  44 ++++
>  security/keys/keyctl.c               |  10 +
>  9 files changed, 513 insertions(+)
>  create mode 100644 security/keys/compat_ecdh.c
>  create mode 100644 security/keys/ecdh.c
>
> diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst
> index 326b8a973828..9749466a3c95 100644
> --- a/Documentation/security/keys/core.rst
> +++ b/Documentation/security/keys/core.rst
> @@ -884,6 +884,68 @@ The keyctl syscall functions are:
>       and either the buffer length or the OtherInfo length exceeds the
>       allowed length.
>  
> +  *  Compute a elliptic curve Diffie-Hellman shared secret::
        Compute an Elliptic Curve Diffie-Hellman (ECDH) shared secret:

> +
> +	long keyctl(KEYCTL_ECDH_COMPUTE, struct keyctl_ecdh_params *params,
> +		    char *buffer, size_t buflen,
> +		    struct keyctl_curve_params *curve);
> +
> +     The params struct contains serial numbers for two keys::
> +
> +	 - The local private key
> +	 - The shared public key
> +
> +     The value computed is::
> +
> +	result = private EC-MUTIPLY public
> +
> +     The buffer length must be at least the length of the prime, or zero.
> +
> +     If the buffer length is nonzero, the length of the result is
> +     returned when it is successfully calculated and copied in to the
> +     buffer. When the buffer length is zero, the minimum required
> +     buffer length is returned.
> +
> +     The curve parameter struct keyctl_curve_params is as follows:
> +
> +	 - ``char *curvename`` specifies the curve parameter used in
> +	   the following computation.
> +
> +     This function will return error EOPNOTSUPP if the key type is not
> +     supported, error ENOKEY if the key could not be found, or error
> +     EACCES if the key is not readable by the caller.
> +
> +  *  Generate a elliptic curve Diffie-Hellman shared public key::

s/::/:/ 

various locations

> +
> +	long keyctl(KEYCTL_ECDH_GENPUBKEY,
> +		    struct keyctl_ecdh_params *params,
> +		    char *buffer, size_t buflen,
> +		    struct keyctl_curve_params *curve);
> +
> +     The params struct contains serial numbers for one keys::
> +
> +	 - The local private key
> +
> +     The value computed is::
> +
> +	result = private EC-MUTIPLY G
> +
> +     The buffer length must be at least the length of the prime, or zero.
> +
> +     If the buffer length is nonzero, the length of the result is
> +     returned when it is successfully calculated and copied in to the
> +     buffer. When the buffer length is zero, the minimum required
> +     buffer length is returned.
> +
> +     The curve parameter struct keyctl_curve_params is as follows:
> +
> +	 - ``char *curvename`` specifies the curve parameter used in

s/``char *curvename``/char *curvename/

> +	   the following computation.
> +
> +     This function will return error EOPNOTSUPP if the key type is not
> +     supported, error ENOKEY if the key could not be found, or error
> +     EACCES if the key is not readable by the caller.
> +
>  
>    *  Restrict keyring linkage::
>  
> diff --git a/include/linux/compat.h b/include/linux/compat.h
> index 233f61ec8afc..1f989ef5c9e1 100644
> --- a/include/linux/compat.h
> +++ b/include/linux/compat.h
> @@ -411,6 +411,10 @@ struct compat_keyctl_kdf_params {
>  	__u32 __spare[8];
>  };
>  
> +struct compat_keyctl_curve_params {
> +	compat_uptr_t curvename;
> +};

Please, do not create this at all.

> +
>  struct compat_stat;
>  struct compat_statfs;
>  struct compat_statfs64;
> diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h
> index 4c8884eea808..77b5d9d837a2 100644
> --- a/include/uapi/linux/keyctl.h
> +++ b/include/uapi/linux/keyctl.h
> @@ -70,6 +70,8 @@
>  #define KEYCTL_MOVE			30	/* Move keys between keyrings */
>  #define KEYCTL_CAPABILITIES		31	/* Find capabilities of keyrings subsystem */
>  #define KEYCTL_WATCH_KEY		32	/* Watch a key or ring of keys for changes */
> +#define KEYCTL_ECDH_COMPUTE		33	/* Compute EC Diffie-Hellman values */
> +#define KEYCTL_ECDH_GENPUBKEY		34	/* Generate EC Diffie-Hellman public keys */
>  
>  /* keyctl structures */
>  struct keyctl_dh_params {
> @@ -90,6 +92,15 @@ struct keyctl_kdf_params {
>  	__u32 __spare[8];
>  };
>  
> +struct keyctl_ecdh_params {
> +	__s32 priv;
> +	__s32 pub;
> +};
> +
> +struct keyctl_curve_params {
> +	char __user *curvename;

This will cause ABI to be a total trainwreck because the size changes
depending on arch bitsize. Please never do anything like this in an
ioctl API.

I.e. please change to __u64 curve_name

> +};
>
> +
>  #define KEYCTL_SUPPORTS_ENCRYPT		0x01
>  #define KEYCTL_SUPPORTS_DECRYPT		0x02
>  #define KEYCTL_SUPPORTS_SIGN		0x04
> diff --git a/security/keys/Kconfig b/security/keys/Kconfig
> index abb03a1b2a5c..b36be8d8d501 100644
> --- a/security/keys/Kconfig
> +++ b/security/keys/Kconfig
> @@ -125,6 +125,18 @@ config KEY_DH_OPERATIONS
>  
>  	 If you are unsure as to whether this is required, answer N.
>  
> +config KEY_ECDH_OPERATIONS
> +       bool "Elliptic Curve Diffie-Hellman operations on retained keys"
> +       depends on KEYS
> +       select CRYPTO
> +       select CRYPTO_ECDH
> +       help
> +	 This option provides support for calculating Elliptic Curve
> +	 Diffie-Hellman public keys and shared secrets using values
> +	 stored as keys in the kernel.
> +
> +	 If you are unsure as to whether this is required, answer N.
> +
>  config KEY_NOTIFICATIONS
>  	bool "Provide key/keyring change notifications"
>  	depends on KEYS && WATCH_QUEUE
> diff --git a/security/keys/Makefile b/security/keys/Makefile
> index 5f40807f05b3..590fc4724f37 100644
> --- a/security/keys/Makefile
> +++ b/security/keys/Makefile
> @@ -17,11 +17,13 @@ obj-y := \
>  	request_key_auth.o \
>  	user_defined.o
>  compat-obj-$(CONFIG_KEY_DH_OPERATIONS) += compat_dh.o
> +compat-obj-$(CONFIG_KEY_ECDH_OPERATIONS) += compat_ecdh.o
>  obj-$(CONFIG_COMPAT) += compat.o $(compat-obj-y)
>  obj-$(CONFIG_PROC_FS) += proc.o
>  obj-$(CONFIG_SYSCTL) += sysctl.o
>  obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
>  obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
> +obj-$(CONFIG_KEY_ECDH_OPERATIONS) += ecdh.o
>  obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o
>  
>  #
> diff --git a/security/keys/compat_ecdh.c b/security/keys/compat_ecdh.c
> new file mode 100644
> index 000000000000..040d2a1c5548
> --- /dev/null
> +++ b/security/keys/compat_ecdh.c
> @@ -0,0 +1,50 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/* 32-bit compatibility syscall for 64-bit systems for ECDH operations
> + *
> + * Copyright (C) 2024 Zhang Yiqun <zhangyiqun@phytium.com.cn>
> + */
> +
> +#include <linux/uaccess.h>
> +

Not sure what is the purpose of this empty line?

> +#include "internal.h"
> +
> +/*
> + * Perform the ECDH computation or ECDH based key derivation.
> + *
> + * If successful, 0 will be returned.
> + */
> +long compat_keyctl_ecdh_compute(struct keyctl_ecdh_params __user *params,
> +				char __user *buffer, size_t buflen,
> +				struct compat_keyctl_curve_params __user *curve)
> +{
> +	struct keyctl_curve_params curvecopy;
> +	struct compat_keyctl_curve_params compat_curvecopy;

reverse xmas tree declaration order (various locations)

> +
> +	if (!curve)
> +		return -EINVAL;
> +
> +	if (copy_from_user(&compat_curvecopy, curve, sizeof(compat_curvecopy)) != 0)
> +		return -EFAULT;
> +
> +	curvecopy.curvename = compat_ptr(compat_curvecopy.curvename);
> +
> +	return keyctl_ecdh_compute(params, buffer, buflen, &curvecopy);
> +}
> +
> +long compat_keyctl_ecdh_genpubkey(struct keyctl_ecdh_params __user *params,
> +				  char __user *buffer, size_t buflen,
> +				  struct compat_keyctl_curve_params __user *curve)
> +{
> +	struct keyctl_curve_params curvecopy;
> +	struct compat_keyctl_curve_params compat_curvecopy;
> +
> +	if (!curve)
> +		return -EINVAL;
> +
> +	if (copy_from_user(&compat_curvecopy, curve, sizeof(compat_curvecopy)) != 0)
> +		return -EFAULT;
> +
> +	curvecopy.curvename = compat_ptr(compat_curvecopy.curvename);
> +
> +	return keyctl_ecdh_genpubkey(params, buffer, buflen, &curvecopy);
> +}
> diff --git a/security/keys/ecdh.c b/security/keys/ecdh.c
> new file mode 100644
> index 000000000000..5e5be22b920c
> --- /dev/null
> +++ b/security/keys/ecdh.c
> @@ -0,0 +1,318 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/* Crypto operations using stored keys
> + *
> + * Copyright (c) 2024 Zhang Yiqun <zhangyiqun@phytium.com.cn>
> + */
> +
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/scatterlist.h>
> +#include <linux/crypto.h>
> +#include <crypto/hash.h>
> +#include <crypto/kpp.h>
> +#include <crypto/ecdh.h>
> +#include <keys/user-type.h>
> +#include "internal.h"
> +
> +static ssize_t ecdh_data_from_key(key_serial_t keyid, char **data)
> +{
> +	struct key *key;
> +	key_ref_t key_ref;
> +	long status;
> +	ssize_t ret;
> +
> +	key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ);
> +	if (IS_ERR(key_ref)) {
> +		ret = -ENOKEY;
> +		goto error;
> +	}
> +
> +	key = key_ref_to_ptr(key_ref);
> +
> +	ret = -EOPNOTSUPP;
> +	if (key->type == &key_type_user) {
> +		down_read(&key->sem);
> +		status = key_validate(key);
> +		if (status == 0) {
> +			const struct user_key_payload *payload;
> +			uint8_t *duplicate;
> +
> +			payload = user_key_payload_locked(key);
> +
> +			duplicate = kmemdup(payload->data, payload->datalen,
> +					    GFP_KERNEL);
> +			if (duplicate) {
> +				*data = duplicate;
> +				ret = payload->datalen;
> +			} else {
> +				ret = -ENOMEM;
> +			}
> +		}
> +		up_read(&key->sem);
> +	}
> +
> +	key_put(key);
> +error:
> +	return ret;
> +}
> +
> +static void ecdh_free_data(struct ecdh *ecdh)
> +{
> +	kfree_sensitive(ecdh->key);
> +}
> +
> +long keyctl_ecdh_compute(struct keyctl_ecdh_params __user *params,
> +		       char __user *buffer, size_t buflen,
> +		       struct keyctl_curve_params __user *curve)
> +{
> +	long ret;
> +	ssize_t dlen;
> +	int secretlen;
> +	int outlen;
> +	struct keyctl_ecdh_params pcopy;
> +	struct ecdh ecdh_inputs;
> +	struct scatterlist insg;

in_sg

> +	struct scatterlist outsg;

out_sg

> +	DECLARE_CRYPTO_WAIT(compl);
> +	struct crypto_kpp *tfm;
> +	struct kpp_request *req;
> +	uint8_t *secret;
> +	uint8_t *outbuf;

out_buf

> +	char *dhname;
> +
> +	if (!params || (!buffer && buflen) || !curve) {
> +		ret = -EINVAL;
> +		goto out1;
> +	}

return -EINVAL; (remove out1 label)

> +
> +	if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) {
> +		ret = -EFAULT;
> +		goto out1;
> +	}
> +
> +	memset(&ecdh_inputs, 0, sizeof(ecdh_inputs));
> +
> +	dlen = ecdh_data_from_key(pcopy.priv, &ecdh_inputs.key);
> +	if (dlen < 0) {
> +		ret = dlen;
> +		goto out1;
> +	}
> +	ecdh_inputs.key_size = dlen;
> +
> +	secretlen = crypto_ecdh_key_len(&ecdh_inputs);
> +	secret = kmalloc(secretlen, GFP_KERNEL);
> +	if (!secret) {
> +		ret = -ENOMEM;
> +		goto out2;

goto err_free_data;

> +	}
> +
> +	ret = crypto_ecdh_encode_key(secret, secretlen, &ecdh_inputs);
> +	if (ret)
> +		goto out3;


goto err_free_secret;

... you probably get the idea, please do this for all labels.

> +
> +	dhname = strndup_user(curve->curvename, CRYPTO_MAX_ALG_NAME);
> +
> +	tfm = crypto_alloc_kpp(dhname, 0, 0);
> +	if (IS_ERR(tfm)) {
> +		ret = PTR_ERR(tfm);
> +		goto out3;


So who free's dhname in this case?

BR, Jarkko
James Bottomley March 30, 2024, 1:09 p.m. UTC | #3
On Sat, 2024-03-30 at 00:04 -0700, Eric Biggers wrote:
> [+Cc linux-crypto]
> 
> On Sat, Mar 30, 2024 at 02:55:06PM +0800, Zhang Yiqun wrote:
> > This patch is to introduce ECDH into keyctl syscall for
> > userspace usage, containing public key generation and
> > shared secret computation.
> > 
> > It is mainly based on dh code, so it has the same condition
> > to the input which only user keys is supported. The output
> > result is storing into the buffer with the provided length.
> > 
> > Signed-off-by: Zhang Yiqun <zhangyiqun@phytium.com.cn>
> > ---
> >  Documentation/security/keys/core.rst |  62 ++++++
> >  include/linux/compat.h               |   4 +
> >  include/uapi/linux/keyctl.h          |  11 +
> >  security/keys/Kconfig                |  12 +
> >  security/keys/Makefile               |   2 +
> >  security/keys/compat_ecdh.c          |  50 +++++
> >  security/keys/ecdh.c                 | 318
> > +++++++++++++++++++++++++++
> >  security/keys/internal.h             |  44 ++++
> >  security/keys/keyctl.c               |  10 +
> >  9 files changed, 513 insertions(+)
> >  create mode 100644 security/keys/compat_ecdh.c
> >  create mode 100644 security/keys/ecdh.c
> 
> Nacked-by: Eric Biggers <ebiggers@google.com>
> 
> The existing KEYCTL_PKEY_*, KEYCTL_DH_COMPUTE, and AF_ALG are causing
> enough problems.  We do not need any more UAPIs like this.  They are
> hard to maintain, break often, not properly documented, increase the
> kernel's attack surface, and what they do is better done in
> userspace.

Actually that's not entirely true.  There is a use case for keys which
is where you'd like to harden unwrapped key handling and don't have the
ability to use a device.  The kernel provides a harder exfiltration
environment than user space, so there is a use case for getting the
kernel to handle operations on unwrapped keys for the protection it
affords the crytpographic key material.

For instance there are people who use the kernel keyring to replace
ssh-agent and thus *reduce* the attack surface they have for storing
ssh keys:

https://blog.cloudflare.com/the-linux-kernel-key-retention-service-and-why-you-should-use-it-in-your-next-application/

The same thing could be done with gpg keys or the gnome keyring.

> Please refer to the recent thread
> https://lore.kernel.org/linux-crypto/CZSHRUIJ4RKL.34T4EASV5DNJM@matfyz.cz/T/#u
> where these issues were discussed in detail.

This thread was talking about using the kernel for handling the
algorithms themselves (which is probably best done in userspace) and
didn't address using the kernel to harden the key protection
environment.

James
Eric Biggers March 31, 2024, 12:48 a.m. UTC | #4
On Sat, Mar 30, 2024 at 09:09:51AM -0400, James Bottomley wrote:
> On Sat, 2024-03-30 at 00:04 -0700, Eric Biggers wrote:
> > [+Cc linux-crypto]
> > 
> > On Sat, Mar 30, 2024 at 02:55:06PM +0800, Zhang Yiqun wrote:
> > > This patch is to introduce ECDH into keyctl syscall for
> > > userspace usage, containing public key generation and
> > > shared secret computation.
> > > 
> > > It is mainly based on dh code, so it has the same condition
> > > to the input which only user keys is supported. The output
> > > result is storing into the buffer with the provided length.
> > > 
> > > Signed-off-by: Zhang Yiqun <zhangyiqun@phytium.com.cn>
> > > ---
> > >  Documentation/security/keys/core.rst |  62 ++++++
> > >  include/linux/compat.h               |   4 +
> > >  include/uapi/linux/keyctl.h          |  11 +
> > >  security/keys/Kconfig                |  12 +
> > >  security/keys/Makefile               |   2 +
> > >  security/keys/compat_ecdh.c          |  50 +++++
> > >  security/keys/ecdh.c                 | 318
> > > +++++++++++++++++++++++++++
> > >  security/keys/internal.h             |  44 ++++
> > >  security/keys/keyctl.c               |  10 +
> > >  9 files changed, 513 insertions(+)
> > >  create mode 100644 security/keys/compat_ecdh.c
> > >  create mode 100644 security/keys/ecdh.c
> > 
> > Nacked-by: Eric Biggers <ebiggers@google.com>
> > 
> > The existing KEYCTL_PKEY_*, KEYCTL_DH_COMPUTE, and AF_ALG are causing
> > enough problems.  We do not need any more UAPIs like this.  They are
> > hard to maintain, break often, not properly documented, increase the
> > kernel's attack surface, and what they do is better done in
> > userspace.
> 
> Actually that's not entirely true.  There is a use case for keys which
> is where you'd like to harden unwrapped key handling and don't have the
> ability to use a device.  The kernel provides a harder exfiltration
> environment than user space, so there is a use case for getting the
> kernel to handle operations on unwrapped keys for the protection it
> affords the crytpographic key material.
> 
> For instance there are people who use the kernel keyring to replace
> ssh-agent and thus *reduce* the attack surface they have for storing
> ssh keys:
> 
> https://blog.cloudflare.com/the-linux-kernel-key-retention-service-and-why-you-should-use-it-in-your-next-application/
> 
> The same thing could be done with gpg keys or the gnome keyring.

First, that blog post never actually said that the "replace ssh-agent with
kernel keyrings" idea was deployed.  It sounds like a proof of concept idea that
someone thought was interesting and decided to blog about.  Upstream OpenSSH has
no support for Linux keyrings.  It seems unlikely it would get added, especially
given the OpenSSH developers' healthy skepticism of using broken Linux-isms.
You're welcome to bring it up on openssh-unix-dev and get their buy-in first.

Second, as mentioned by the blog post, the kernel also does not support private
keys in the default OpenSSH format.  That sort of thing is an example of the
fundamental problem with trying to make the kernel support every cryptographic
protocol and format in existence.  Userspace simply has much more flexibility to
implement whatever it happens to need.

Third, ssh-agent is already a separate process, and like any other process the
kernel enforces isolation of its address space.  The potential loopholes are
ptrace and coredumps, which ssh-agent already disables, except for ptrace by
root which it can't do alone, but the system administrator can do that by
setting the ptrace_scope sysctl to 3 or by using SELinux.

Amusingly, the existing KEYCTL_DH_* APIs, and the KEYCTL_ECDH_* APIs proposed by
this patch, only operate on user keys that the process has READ access to.  This
means that the keys can be trivially extracted by a shell script running in your
user session.  That's *less* secure than using an isolated process...

That's not to mention that merging this will likely add vulnerabilities
reachable by unprivileged users, just as the original KEYCTL_DH_* did.  I had to
fix some of them last time around (e.g. commits 12d41a023efb, bbe240454d86,
3619dec5103d, 1d9ddde12e3c, ccd9888f14a8, 199512b1234f).  I don't really feel
like doing it again. (Wait, was this supposed to *improve* security?)

> > Please refer to the recent thread
> > https://lore.kernel.org/linux-crypto/CZSHRUIJ4RKL.34T4EASV5DNJM@matfyz.cz/T/#u
> > where these issues were discussed in detail.
> 
> This thread was talking about using the kernel for handling the
> algorithms themselves (which is probably best done in userspace) and
> didn't address using the kernel to harden the key protection
> environment.

This patch is about using the kernel to handle a class of algorithm,
Elliptic-Curve Diffie-Hellman.  Which specific algorithm in that class (i.e.
which elliptic curve), who knows.  Just like the existing APIs like this, it's
undocumented which algorithm(s) are actually supported.

- Eric
Denis Kenzior March 31, 2024, 2:38 a.m. UTC | #5
Hi Eric,

> 
> Amusingly, the existing KEYCTL_DH_* APIs, and the KEYCTL_ECDH_* APIs proposed by
> this patch, only operate on user keys that the process has READ access to.  This
> means that the keys can be trivially extracted by a shell script running in your
> user session.  That's *less* secure than using an isolated process...
> 

I can see this being true for user or session keys, but I don't think this is 
true of process or thread specific keys.  At least I couldn't read any keys out 
of a test app when I tried.

Regards,
-Denis
James Bottomley March 31, 2024, 1:01 p.m. UTC | #6
On Sat, 2024-03-30 at 17:48 -0700, Eric Biggers wrote:
> On Sat, Mar 30, 2024 at 09:09:51AM -0400, James Bottomley wrote:
[...]
> > For instance there are people who use the kernel keyring to replace
> > ssh-agent and thus *reduce* the attack surface they have for
> > storing
> > ssh keys:
> > 
> > https://blog.cloudflare.com/the-linux-kernel-key-retention-service-and-why-you-should-use-it-in-your-next-application/
> > 
> > The same thing could be done with gpg keys or the gnome keyring.
> 
> First, that blog post never actually said that the "replace ssh-agent
> with kernel keyrings" idea was deployed.  It sounds like a proof of
> concept idea that someone thought was interesting and decided to blog
> about.  Upstream OpenSSH has no support for Linux keyrings.

The openssh community is incredibly resistant to out of house
innovation.  It has no support for engine or provider keys, for TPM
keys, or for that systemd start patch xz just exploited ...

>   It seems unlikely it would get added, especially given the OpenSSH
> developers' healthy skepticism of using broken Linux-isms.
> You're welcome to bring it up on openssh-unix-dev and get their buy-
> in first.

I also didn't say just openssh.  You picked the one you apparently know
hardly ever accepts anyone else's ideas.  I don't disagree that finding
implementors is reasonable ... I just wouldn't pick openssh as the
first upstream target.

> Second, as mentioned by the blog post, the kernel also does not
> support private keys in the default OpenSSH format.  That sort of
> thing is an example of the fundamental problem with trying to make
> the kernel support every cryptographic protocol and format in
> existence.  Userspace simply has much more flexibility to implement
> whatever it happens to need.

That's a complete red herring.  You don't need the kernel keyrings to
support every format, you just need a user space converter to import to
the kernel keyring format.  Every device or token that can replace key
handling has their own internal format and they all come with importers
that do conversion.

> Third, ssh-agent is already a separate process, and like any other
> process the kernel enforces isolation of its address space.  The
> potential loopholes are ptrace and coredumps, which ssh-agent already
> disables, except for ptrace by root which it can't do alone, but the
> system administrator can do that by setting the ptrace_scope sysctl
> to 3 or by using SELinux.

Well, a) this doesn't survive privilege escalation and b) I don't think
many people would buy into the notion that we should remove security
functions from the kernel and give them to userspace daemons because
it's safer.

James
Jarkko Sakkinen March 31, 2024, 3:44 p.m. UTC | #7
On Sat Mar 30, 2024 at 3:09 PM EET, James Bottomley wrote:
> On Sat, 2024-03-30 at 00:04 -0700, Eric Biggers wrote:
> > [+Cc linux-crypto]
> > 
> > On Sat, Mar 30, 2024 at 02:55:06PM +0800, Zhang Yiqun wrote:
> > > This patch is to introduce ECDH into keyctl syscall for
> > > userspace usage, containing public key generation and
> > > shared secret computation.
> > > 
> > > It is mainly based on dh code, so it has the same condition
> > > to the input which only user keys is supported. The output
> > > result is storing into the buffer with the provided length.
> > > 
> > > Signed-off-by: Zhang Yiqun <zhangyiqun@phytium.com.cn>
> > > ---
> > >  Documentation/security/keys/core.rst |  62 ++++++
> > >  include/linux/compat.h               |   4 +
> > >  include/uapi/linux/keyctl.h          |  11 +
> > >  security/keys/Kconfig                |  12 +
> > >  security/keys/Makefile               |   2 +
> > >  security/keys/compat_ecdh.c          |  50 +++++
> > >  security/keys/ecdh.c                 | 318
> > > +++++++++++++++++++++++++++
> > >  security/keys/internal.h             |  44 ++++
> > >  security/keys/keyctl.c               |  10 +
> > >  9 files changed, 513 insertions(+)
> > >  create mode 100644 security/keys/compat_ecdh.c
> > >  create mode 100644 security/keys/ecdh.c
> > 
> > Nacked-by: Eric Biggers <ebiggers@google.com>
> > 
> > The existing KEYCTL_PKEY_*, KEYCTL_DH_COMPUTE, and AF_ALG are causing
> > enough problems.  We do not need any more UAPIs like this.  They are
> > hard to maintain, break often, not properly documented, increase the
> > kernel's attack surface, and what they do is better done in
> > userspace.
>
> Actually that's not entirely true.  There is a use case for keys which
> is where you'd like to harden unwrapped key handling and don't have the
> ability to use a device.  The kernel provides a harder exfiltration
> environment than user space, so there is a use case for getting the
> kernel to handle operations on unwrapped keys for the protection it
> affords the crytpographic key material.
>
> For instance there are people who use the kernel keyring to replace
> ssh-agent and thus *reduce* the attack surface they have for storing
> ssh keys:
>
> https://blog.cloudflare.com/the-linux-kernel-key-retention-service-and-why-you-should-use-it-in-your-next-application/
>
> The same thing could be done with gpg keys or the gnome keyring.

Eric has a correct standing given that the commit message does not have
motivation part at all. 

With a description of the problem that this patch is supposed to solve
this would be more meaningful to review.

BR, Jarkko
diff mbox series

Patch

diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst
index 326b8a973828..9749466a3c95 100644
--- a/Documentation/security/keys/core.rst
+++ b/Documentation/security/keys/core.rst
@@ -884,6 +884,68 @@  The keyctl syscall functions are:
      and either the buffer length or the OtherInfo length exceeds the
      allowed length.
 
+  *  Compute a elliptic curve Diffie-Hellman shared secret::
+
+	long keyctl(KEYCTL_ECDH_COMPUTE, struct keyctl_ecdh_params *params,
+		    char *buffer, size_t buflen,
+		    struct keyctl_curve_params *curve);
+
+     The params struct contains serial numbers for two keys::
+
+	 - The local private key
+	 - The shared public key
+
+     The value computed is::
+
+	result = private EC-MUTIPLY public
+
+     The buffer length must be at least the length of the prime, or zero.
+
+     If the buffer length is nonzero, the length of the result is
+     returned when it is successfully calculated and copied in to the
+     buffer. When the buffer length is zero, the minimum required
+     buffer length is returned.
+
+     The curve parameter struct keyctl_curve_params is as follows:
+
+	 - ``char *curvename`` specifies the curve parameter used in
+	   the following computation.
+
+     This function will return error EOPNOTSUPP if the key type is not
+     supported, error ENOKEY if the key could not be found, or error
+     EACCES if the key is not readable by the caller.
+
+  *  Generate a elliptic curve Diffie-Hellman shared public key::
+
+	long keyctl(KEYCTL_ECDH_GENPUBKEY,
+		    struct keyctl_ecdh_params *params,
+		    char *buffer, size_t buflen,
+		    struct keyctl_curve_params *curve);
+
+     The params struct contains serial numbers for one keys::
+
+	 - The local private key
+
+     The value computed is::
+
+	result = private EC-MUTIPLY G
+
+     The buffer length must be at least the length of the prime, or zero.
+
+     If the buffer length is nonzero, the length of the result is
+     returned when it is successfully calculated and copied in to the
+     buffer. When the buffer length is zero, the minimum required
+     buffer length is returned.
+
+     The curve parameter struct keyctl_curve_params is as follows:
+
+	 - ``char *curvename`` specifies the curve parameter used in
+	   the following computation.
+
+     This function will return error EOPNOTSUPP if the key type is not
+     supported, error ENOKEY if the key could not be found, or error
+     EACCES if the key is not readable by the caller.
+
 
   *  Restrict keyring linkage::
 
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 233f61ec8afc..1f989ef5c9e1 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -411,6 +411,10 @@  struct compat_keyctl_kdf_params {
 	__u32 __spare[8];
 };
 
+struct compat_keyctl_curve_params {
+	compat_uptr_t curvename;
+};
+
 struct compat_stat;
 struct compat_statfs;
 struct compat_statfs64;
diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h
index 4c8884eea808..77b5d9d837a2 100644
--- a/include/uapi/linux/keyctl.h
+++ b/include/uapi/linux/keyctl.h
@@ -70,6 +70,8 @@ 
 #define KEYCTL_MOVE			30	/* Move keys between keyrings */
 #define KEYCTL_CAPABILITIES		31	/* Find capabilities of keyrings subsystem */
 #define KEYCTL_WATCH_KEY		32	/* Watch a key or ring of keys for changes */
+#define KEYCTL_ECDH_COMPUTE		33	/* Compute EC Diffie-Hellman values */
+#define KEYCTL_ECDH_GENPUBKEY		34	/* Generate EC Diffie-Hellman public keys */
 
 /* keyctl structures */
 struct keyctl_dh_params {
@@ -90,6 +92,15 @@  struct keyctl_kdf_params {
 	__u32 __spare[8];
 };
 
+struct keyctl_ecdh_params {
+	__s32 priv;
+	__s32 pub;
+};
+
+struct keyctl_curve_params {
+	char __user *curvename;
+};
+
 #define KEYCTL_SUPPORTS_ENCRYPT		0x01
 #define KEYCTL_SUPPORTS_DECRYPT		0x02
 #define KEYCTL_SUPPORTS_SIGN		0x04
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index abb03a1b2a5c..b36be8d8d501 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -125,6 +125,18 @@  config KEY_DH_OPERATIONS
 
 	 If you are unsure as to whether this is required, answer N.
 
+config KEY_ECDH_OPERATIONS
+       bool "Elliptic Curve Diffie-Hellman operations on retained keys"
+       depends on KEYS
+       select CRYPTO
+       select CRYPTO_ECDH
+       help
+	 This option provides support for calculating Elliptic Curve
+	 Diffie-Hellman public keys and shared secrets using values
+	 stored as keys in the kernel.
+
+	 If you are unsure as to whether this is required, answer N.
+
 config KEY_NOTIFICATIONS
 	bool "Provide key/keyring change notifications"
 	depends on KEYS && WATCH_QUEUE
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 5f40807f05b3..590fc4724f37 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -17,11 +17,13 @@  obj-y := \
 	request_key_auth.o \
 	user_defined.o
 compat-obj-$(CONFIG_KEY_DH_OPERATIONS) += compat_dh.o
+compat-obj-$(CONFIG_KEY_ECDH_OPERATIONS) += compat_ecdh.o
 obj-$(CONFIG_COMPAT) += compat.o $(compat-obj-y)
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
 obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
 obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
+obj-$(CONFIG_KEY_ECDH_OPERATIONS) += ecdh.o
 obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o
 
 #
diff --git a/security/keys/compat_ecdh.c b/security/keys/compat_ecdh.c
new file mode 100644
index 000000000000..040d2a1c5548
--- /dev/null
+++ b/security/keys/compat_ecdh.c
@@ -0,0 +1,50 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* 32-bit compatibility syscall for 64-bit systems for ECDH operations
+ *
+ * Copyright (C) 2024 Zhang Yiqun <zhangyiqun@phytium.com.cn>
+ */
+
+#include <linux/uaccess.h>
+
+#include "internal.h"
+
+/*
+ * Perform the ECDH computation or ECDH based key derivation.
+ *
+ * If successful, 0 will be returned.
+ */
+long compat_keyctl_ecdh_compute(struct keyctl_ecdh_params __user *params,
+				char __user *buffer, size_t buflen,
+				struct compat_keyctl_curve_params __user *curve)
+{
+	struct keyctl_curve_params curvecopy;
+	struct compat_keyctl_curve_params compat_curvecopy;
+
+	if (!curve)
+		return -EINVAL;
+
+	if (copy_from_user(&compat_curvecopy, curve, sizeof(compat_curvecopy)) != 0)
+		return -EFAULT;
+
+	curvecopy.curvename = compat_ptr(compat_curvecopy.curvename);
+
+	return keyctl_ecdh_compute(params, buffer, buflen, &curvecopy);
+}
+
+long compat_keyctl_ecdh_genpubkey(struct keyctl_ecdh_params __user *params,
+				  char __user *buffer, size_t buflen,
+				  struct compat_keyctl_curve_params __user *curve)
+{
+	struct keyctl_curve_params curvecopy;
+	struct compat_keyctl_curve_params compat_curvecopy;
+
+	if (!curve)
+		return -EINVAL;
+
+	if (copy_from_user(&compat_curvecopy, curve, sizeof(compat_curvecopy)) != 0)
+		return -EFAULT;
+
+	curvecopy.curvename = compat_ptr(compat_curvecopy.curvename);
+
+	return keyctl_ecdh_genpubkey(params, buffer, buflen, &curvecopy);
+}
diff --git a/security/keys/ecdh.c b/security/keys/ecdh.c
new file mode 100644
index 000000000000..5e5be22b920c
--- /dev/null
+++ b/security/keys/ecdh.c
@@ -0,0 +1,318 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Crypto operations using stored keys
+ *
+ * Copyright (c) 2024 Zhang Yiqun <zhangyiqun@phytium.com.cn>
+ */
+
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <crypto/hash.h>
+#include <crypto/kpp.h>
+#include <crypto/ecdh.h>
+#include <keys/user-type.h>
+#include "internal.h"
+
+static ssize_t ecdh_data_from_key(key_serial_t keyid, char **data)
+{
+	struct key *key;
+	key_ref_t key_ref;
+	long status;
+	ssize_t ret;
+
+	key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ);
+	if (IS_ERR(key_ref)) {
+		ret = -ENOKEY;
+		goto error;
+	}
+
+	key = key_ref_to_ptr(key_ref);
+
+	ret = -EOPNOTSUPP;
+	if (key->type == &key_type_user) {
+		down_read(&key->sem);
+		status = key_validate(key);
+		if (status == 0) {
+			const struct user_key_payload *payload;
+			uint8_t *duplicate;
+
+			payload = user_key_payload_locked(key);
+
+			duplicate = kmemdup(payload->data, payload->datalen,
+					    GFP_KERNEL);
+			if (duplicate) {
+				*data = duplicate;
+				ret = payload->datalen;
+			} else {
+				ret = -ENOMEM;
+			}
+		}
+		up_read(&key->sem);
+	}
+
+	key_put(key);
+error:
+	return ret;
+}
+
+static void ecdh_free_data(struct ecdh *ecdh)
+{
+	kfree_sensitive(ecdh->key);
+}
+
+long keyctl_ecdh_compute(struct keyctl_ecdh_params __user *params,
+		       char __user *buffer, size_t buflen,
+		       struct keyctl_curve_params __user *curve)
+{
+	long ret;
+	ssize_t dlen;
+	int secretlen;
+	int outlen;
+	struct keyctl_ecdh_params pcopy;
+	struct ecdh ecdh_inputs;
+	struct scatterlist insg;
+	struct scatterlist outsg;
+	DECLARE_CRYPTO_WAIT(compl);
+	struct crypto_kpp *tfm;
+	struct kpp_request *req;
+	uint8_t *secret;
+	uint8_t *outbuf;
+	char *dhname;
+
+	if (!params || (!buffer && buflen) || !curve) {
+		ret = -EINVAL;
+		goto out1;
+	}
+
+	if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) {
+		ret = -EFAULT;
+		goto out1;
+	}
+
+	memset(&ecdh_inputs, 0, sizeof(ecdh_inputs));
+
+	dlen = ecdh_data_from_key(pcopy.priv, &ecdh_inputs.key);
+	if (dlen < 0) {
+		ret = dlen;
+		goto out1;
+	}
+	ecdh_inputs.key_size = dlen;
+
+	secretlen = crypto_ecdh_key_len(&ecdh_inputs);
+	secret = kmalloc(secretlen, GFP_KERNEL);
+	if (!secret) {
+		ret = -ENOMEM;
+		goto out2;
+	}
+
+	ret = crypto_ecdh_encode_key(secret, secretlen, &ecdh_inputs);
+	if (ret)
+		goto out3;
+
+	dhname = strndup_user(curve->curvename, CRYPTO_MAX_ALG_NAME);
+
+	tfm = crypto_alloc_kpp(dhname, 0, 0);
+	if (IS_ERR(tfm)) {
+		ret = PTR_ERR(tfm);
+		goto out3;
+	}
+
+	kfree(dhname);
+
+	ret = crypto_kpp_set_secret(tfm, secret, secretlen);
+	if (ret)
+		goto out4;
+
+	outlen = crypto_kpp_maxsize(tfm);
+
+	if (buflen == 0) {
+		ret = outlen;
+		goto out4;
+	} else if (outlen > buflen) {
+		ret = -EOVERFLOW;
+		goto out4;
+	}
+
+	outbuf = kzalloc(outlen, GFP_KERNEL);
+	if (!outbuf) {
+		ret = -ENOMEM;
+		goto out4;
+	}
+
+	dlen = ecdh_data_from_key(pcopy.pub, (char **)&outbuf);
+	if (dlen != outlen) {
+		ret = dlen;
+		goto out5;
+	}
+
+	sg_init_one(&insg, outbuf, outlen);
+	sg_init_one(&outsg, outbuf, outlen/2);
+
+	req = kpp_request_alloc(tfm, GFP_KERNEL);
+	if (!req) {
+		ret = -ENOMEM;
+		goto out5;
+	}
+
+	kpp_request_set_input(req, &insg, outlen);
+	kpp_request_set_output(req, &outsg, outlen/2);
+	init_completion(&compl.completion);
+	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+				 CRYPTO_TFM_REQ_MAY_SLEEP,
+				 crypto_req_done, &compl);
+
+	/*
+	 * For DH, generate_public_key and generate_shared_secret are
+	 * the same calculation
+	 */
+	ret = crypto_kpp_compute_shared_secret(req);
+	if (ret == -EINPROGRESS) {
+		wait_for_completion(&compl.completion);
+		ret = compl.err;
+		if (ret)
+			goto out6;
+	}
+
+	if (copy_to_user(buffer, outbuf, req->dst_len) == 0)
+		ret = req->dst_len;
+	else
+		ret = -EFAULT;
+
+out6:
+	kpp_request_free(req);
+out5:
+	kfree_sensitive(outbuf);
+out4:
+	crypto_free_kpp(tfm);
+out3:
+	kfree_sensitive(secret);
+out2:
+	ecdh_free_data(&ecdh_inputs);
+out1:
+	return ret;
+}
+
+long keyctl_ecdh_genpubkey(struct keyctl_ecdh_params __user *params,
+		       char __user *buffer, size_t buflen,
+		       struct keyctl_curve_params __user *curve)
+{
+	long ret;
+	ssize_t dlen;
+	int secretlen;
+	int outlen;
+	struct keyctl_ecdh_params pcopy;
+	struct ecdh ecdh_inputs;
+	struct scatterlist outsg;
+	DECLARE_CRYPTO_WAIT(compl);
+	struct crypto_kpp *tfm;
+	struct kpp_request *req;
+	uint8_t *secret;
+	uint8_t *outbuf;
+	char *dhname;
+
+	if (!params || (!buffer && buflen)) {
+		ret = -EINVAL;
+		goto out1;
+	}
+
+	if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) {
+		ret = -EFAULT;
+		goto out1;
+	}
+
+	memset(&ecdh_inputs, 0, sizeof(ecdh_inputs));
+
+	dlen = ecdh_data_from_key(pcopy.priv, &ecdh_inputs.key);
+	if (dlen < 0) {
+		ret = dlen;
+		goto out1;
+	}
+	ecdh_inputs.key_size = dlen;
+
+	secretlen = crypto_ecdh_key_len(&ecdh_inputs);
+	secret = kmalloc(secretlen, GFP_KERNEL);
+	if (!secret) {
+		ret = -ENOMEM;
+		goto out2;
+	}
+
+	ret = crypto_ecdh_encode_key(secret, secretlen, &ecdh_inputs);
+	if (ret)
+		goto out3;
+
+	dhname = strndup_user(curve->curvename, CRYPTO_MAX_ALG_NAME);
+
+	tfm = crypto_alloc_kpp(dhname, 0, 0);
+	if (IS_ERR(tfm)) {
+		ret = PTR_ERR(tfm);
+		goto out3;
+	}
+
+	kfree(dhname);
+
+	ret = crypto_kpp_set_secret(tfm, secret, secretlen);
+	if (ret)
+		goto out4;
+
+	outlen = crypto_kpp_maxsize(tfm);
+
+	if (buflen == 0) {
+		ret = outlen;
+		goto out4;
+	} else if (outlen > buflen) {
+		ret = -EOVERFLOW;
+		goto out4;
+	}
+
+	outbuf = kzalloc(outlen, GFP_KERNEL);
+	if (!outbuf) {
+		ret = -ENOMEM;
+		goto out4;
+	}
+
+	sg_init_one(&outsg, outbuf, outlen);
+
+	req = kpp_request_alloc(tfm, GFP_KERNEL);
+	if (!req) {
+		ret = -ENOMEM;
+		goto out5;
+	}
+
+	kpp_request_set_input(req, NULL, 0);
+	kpp_request_set_output(req, &outsg, outlen);
+	init_completion(&compl.completion);
+	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+				 CRYPTO_TFM_REQ_MAY_SLEEP,
+				 crypto_req_done, &compl);
+
+	/*
+	 * For DH, generate_public_key and generate_shared_secret are
+	 * the same calculation
+	 */
+	ret = crypto_kpp_generate_public_key(req);
+	if (ret == -EINPROGRESS) {
+		wait_for_completion(&compl.completion);
+		ret = compl.err;
+		if (ret)
+			goto out6;
+	}
+
+	if (copy_to_user(buffer, outbuf, req->dst_len) == 0)
+		ret = req->dst_len;
+	else
+		ret = -EFAULT;
+
+out6:
+	kpp_request_free(req);
+out5:
+	kfree_sensitive(outbuf);
+out4:
+	crypto_free_kpp(tfm);
+out3:
+	kfree_sensitive(secret);
+out2:
+	ecdh_free_data(&ecdh_inputs);
+out1:
+	return ret;
+}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 2cffa6dc8255..165523e29b52 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -306,6 +306,50 @@  static inline long compat_keyctl_dh_compute(
 #endif
 #endif
 
+#ifdef CONFIG_KEY_ECDH_OPERATIONS
+extern long keyctl_ecdh_compute(struct keyctl_ecdh_params __user *param,
+			      char __user *buffer, size_t buflen,
+			      struct keyctl_curve_params __user *curve);
+extern long keyctl_ecdh_genpubkey(struct keyctl_ecdh_params __user *param,
+			      char __user *buffer, size_t buflen,
+			      struct keyctl_curve_params __user *curve);
+#ifdef CONFIG_COMPAT
+extern long compat_keyctl_ecdh_compute(struct keyctl_ecdh_params __user *params,
+				char __user *buffer, size_t buflen,
+				struct compat_keyctl_curve_params __user *curve);
+extern long compat_keyctl_ecdh_genpubkey(struct keyctl_ecdh_params __user *params,
+				char __user *buffer, size_t buflen,
+				struct compat_keyctl_curve_params __user *curve);
+#endif
+#else
+static inline long keyctl_ecdh_compute(struct keyctl_ecdh_params __user *params,
+				     char __user *buffer, size_t buflen,
+				     struct keyctl_curve_params __user *kdf)
+{
+	return -EOPNOTSUPP;
+}
+static inline long keyctl_ecdh_genpubkey(struct keyctl_ecdh_params __user *params,
+				     char __user *buffer, size_t buflen,
+				     struct keyctl_curve_params __user *kdf)
+{
+	return -EOPNOTSUPP;
+}
+#ifdef CONFIG_COMPAT
+long compat_keyctl_ecdh_compute(struct keyctl_ecdh_params __user *params,
+				char __user *buffer, size_t buflen,
+				struct compat_keyctl_curve_params __user *curve)
+{
+	return -EOPNOTSUPP;
+}
+long compat_keyctl_ecdh_genpubkey(struct keyctl_ecdh_params __user *params,
+				char __user *buffer, size_t buflen,
+				struct compat_keyctl_curve_params __user *curve)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+#endif
+
 #ifdef CONFIG_ASYMMETRIC_KEY_TYPE
 extern long keyctl_pkey_query(key_serial_t,
 			      const char __user *,
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 10ba439968f7..e690c130386a 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -2019,6 +2019,16 @@  SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
 	case KEYCTL_WATCH_KEY:
 		return keyctl_watch_key((key_serial_t)arg2, (int)arg3, (int)arg4);
 
+	case KEYCTL_ECDH_COMPUTE:
+		return keyctl_ecdh_compute((struct keyctl_ecdh_params __user *) arg2,
+					 (char __user *) arg3, (size_t) arg4,
+					 (struct keyctl_curve_params __user *) arg5);
+
+	case KEYCTL_ECDH_GENPUBKEY:
+		return keyctl_ecdh_genpubkey((struct keyctl_ecdh_params __user *) arg2,
+					 (char __user *) arg3, (size_t) arg4,
+					 (struct keyctl_curve_params __user *) arg5);
+
 	default:
 		return -EOPNOTSUPP;
 	}