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