Re: [PATCH] KEYS: Add ECDH support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxxx>
> ---
>  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@xxxxxxxxxxxxxx>
> + */
> +
> +#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@xxxxxxxxxxxxxx>
> + */
> +
> +#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






[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux