From: Daniel Hodges <git@danielhodges.dev>
To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org,
vadim.fedorenko@linux.dev
Cc: martin.lau@linux.dev, eddyz87@gmail.com, song@kernel.org,
yonghong.song@linux.dev, john.fastabend@gmail.com,
kpsingh@kernel.org, sdf@fomichev.me, haoluo@google.com,
jolsa@kernel.org, herbert@gondor.apana.org.au,
davem@davemloft.net, shuah@kernel.org, bpf@vger.kernel.org,
linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-kernel@vger.kernel.org,
Daniel Hodges <git@danielhodges.dev>
Subject: [PATCH bpf-next v2 4/5] bpf: Add ECDSA signature verification kfuncs
Date: Fri, 5 Dec 2025 12:39:22 -0500 [thread overview]
Message-ID: <20251205173923.31740-5-git@danielhodges.dev> (raw)
In-Reply-To: <20251205173923.31740-1-git@danielhodges.dev>
Add context-based ECDSA signature verification kfuncs:
- bpf_ecdsa_ctx_create(): Creates reusable ECDSA context with public key
- bpf_ecdsa_verify(): Verifies signatures using the context
- bpf_ecdsa_ctx_acquire(): Increments context reference count
- bpf_ecdsa_ctx_release(): Releases context with RCU safety
The ECDSA implementation supports NIST curves (P-256, P-384, P-521) and
uses the kernel's crypto_sig API. Public keys must be in uncompressed
format (0x04 || x || y), and signatures are in r || s format.
Signed-off-by: Daniel Hodges <git@danielhodges.dev>
---
kernel/bpf/crypto.c | 230 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 230 insertions(+)
diff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c
index 95625c7ffb1a..3a3741cf657f 100644
--- a/kernel/bpf/crypto.c
+++ b/kernel/bpf/crypto.c
@@ -9,6 +9,7 @@
#include <linux/scatterlist.h>
#include <linux/skbuff.h>
#include <crypto/skcipher.h>
+#include <crypto/sig.h>
struct bpf_crypto_type_list {
const struct bpf_crypto_type *type;
@@ -57,6 +58,21 @@ struct bpf_crypto_ctx {
refcount_t usage;
};
+#if IS_ENABLED(CONFIG_CRYPTO_ECDSA)
+/**
+ * struct bpf_ecdsa_ctx - refcounted BPF ECDSA context structure
+ * @tfm: The crypto_sig transform for ECDSA operations
+ * @rcu: The RCU head used to free the context with RCU safety
+ * @usage: Object reference counter. When the refcount goes to 0, the
+ * memory is released with RCU safety.
+ */
+struct bpf_ecdsa_ctx {
+ struct crypto_sig *tfm;
+ struct rcu_head rcu;
+ refcount_t usage;
+};
+#endif
+
int bpf_crypto_register_type(const struct bpf_crypto_type *type)
{
struct bpf_crypto_type_list *node;
@@ -399,12 +415,206 @@ __bpf_kfunc int bpf_crypto_hash(struct bpf_crypto_ctx *ctx,
}
#endif /* CONFIG_CRYPTO_HASH2 */
+#if IS_ENABLED(CONFIG_CRYPTO_ECDSA)
+/**
+ * bpf_ecdsa_ctx_create() - Create a BPF ECDSA verification context
+ * @algo_name: bpf_dynptr to the algorithm name (e.g., "p1363(ecdsa-nist-p256)")
+ * @public_key: bpf_dynptr to the public key in uncompressed format (0x04 || x || y)
+ * Must be 65 bytes for P-256, 97 for P-384, 133 for P-521
+ * @err: Pointer to store error code on failure
+ *
+ * Creates an ECDSA verification context that can be reused for multiple
+ * signature verifications. This function uses GFP_KERNEL allocation and
+ * can only be called from sleepable BPF programs. Uses bpf_dynptr to ensure
+ * safe memory access without risk of page faults.
+ */
+__bpf_kfunc struct bpf_ecdsa_ctx *
+bpf_ecdsa_ctx_create(const struct bpf_dynptr *algo_name,
+ const struct bpf_dynptr *public_key, int *err)
+{
+ const struct bpf_dynptr_kern *algo_kern = (struct bpf_dynptr_kern *)algo_name;
+ const struct bpf_dynptr_kern *key_kern = (struct bpf_dynptr_kern *)public_key;
+ struct bpf_ecdsa_ctx *ctx;
+ const char *algo_ptr;
+ const u8 *key_ptr;
+ u32 algo_len, key_len;
+ char algo[64];
+ int ret;
+
+ if (!err)
+ return NULL;
+
+ algo_len = __bpf_dynptr_size(algo_kern);
+ key_len = __bpf_dynptr_size(key_kern);
+
+ if (algo_len == 0 || algo_len >= sizeof(algo)) {
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ if (key_len < 65) {
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ algo_ptr = __bpf_dynptr_data(algo_kern, algo_len);
+ if (!algo_ptr) {
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ key_ptr = __bpf_dynptr_data(key_kern, key_len);
+ if (!key_ptr) {
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ if (key_ptr[0] != 0x04) {
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ memcpy(algo, algo_ptr, algo_len);
+ algo[algo_len] = '\0';
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ *err = -ENOMEM;
+ return NULL;
+ }
+
+ ctx->tfm = crypto_alloc_sig(algo, 0, 0);
+ if (IS_ERR(ctx->tfm)) {
+ *err = PTR_ERR(ctx->tfm);
+ kfree(ctx);
+ return NULL;
+ }
+
+ ret = crypto_sig_set_pubkey(ctx->tfm, key_ptr, key_len);
+ if (ret) {
+ *err = ret;
+ crypto_free_sig(ctx->tfm);
+ kfree(ctx);
+ return NULL;
+ }
+
+ refcount_set(&ctx->usage, 1);
+ *err = 0;
+ return ctx;
+}
+
+/**
+ * bpf_ecdsa_verify() - Verify ECDSA signature using pre-allocated context
+ * @ctx: ECDSA context created by bpf_ecdsa_ctx_create()
+ * @message: bpf_dynptr to the message hash to verify. Must be a trusted pointer.
+ * @signature: bpf_dynptr to the ECDSA signature in r || s format. Must be a trusted pointer.
+ * Must be 64 bytes for P-256, 96 for P-384, 132 for P-521
+ *
+ * Verifies an ECDSA signature using a pre-allocated context. This function
+ * does not allocate memory and can be used in non-sleepable BPF programs.
+ * Uses bpf_dynptr to ensure safe memory access without risk of page faults.
+ */
+__bpf_kfunc int bpf_ecdsa_verify(struct bpf_ecdsa_ctx *ctx,
+ const struct bpf_dynptr *message,
+ const struct bpf_dynptr *signature)
+{
+ const struct bpf_dynptr_kern *msg_kern = (struct bpf_dynptr_kern *)message;
+ const struct bpf_dynptr_kern *sig_kern = (struct bpf_dynptr_kern *)signature;
+ const u8 *msg_ptr, *sig_ptr;
+ u32 msg_len, sig_len;
+
+ if (!ctx)
+ return -EINVAL;
+
+ msg_len = __bpf_dynptr_size(msg_kern);
+ sig_len = __bpf_dynptr_size(sig_kern);
+
+ if (msg_len == 0 || sig_len == 0)
+ return -EINVAL;
+
+ msg_ptr = __bpf_dynptr_data(msg_kern, msg_len);
+ if (!msg_ptr)
+ return -EINVAL;
+
+ sig_ptr = __bpf_dynptr_data(sig_kern, sig_len);
+ if (!sig_ptr)
+ return -EINVAL;
+
+ return crypto_sig_verify(ctx->tfm, sig_ptr, sig_len, msg_ptr, msg_len);
+}
+
+__bpf_kfunc struct bpf_ecdsa_ctx *
+bpf_ecdsa_ctx_acquire(struct bpf_ecdsa_ctx *ctx)
+{
+ if (!refcount_inc_not_zero(&ctx->usage))
+ return NULL;
+ return ctx;
+}
+
+static void ecdsa_free_cb(struct rcu_head *head)
+{
+ struct bpf_ecdsa_ctx *ctx = container_of(head, struct bpf_ecdsa_ctx, rcu);
+
+ crypto_free_sig(ctx->tfm);
+ kfree(ctx);
+}
+
+__bpf_kfunc void bpf_ecdsa_ctx_release(struct bpf_ecdsa_ctx *ctx)
+{
+ if (refcount_dec_and_test(&ctx->usage))
+ call_rcu(&ctx->rcu, ecdsa_free_cb);
+}
+
+/**
+ * bpf_ecdsa_keysize() - Get the key size for ECDSA context
+ * @ctx: ECDSA context
+ *
+ * Returns: Key size in bits, or negative error code on failure
+ */
+__bpf_kfunc int bpf_ecdsa_keysize(struct bpf_ecdsa_ctx *ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+
+ return crypto_sig_keysize(ctx->tfm);
+}
+
+/**
+ * bpf_ecdsa_digestsize() - Get the maximum digest size for ECDSA context
+ * @ctx: ECDSA context
+ */
+__bpf_kfunc int bpf_ecdsa_digestsize(struct bpf_ecdsa_ctx *ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+
+ return crypto_sig_digestsize(ctx->tfm);
+}
+
+/**
+ * bpf_ecdsa_maxsize() - Get the maximum signature size for ECDSA context
+ * @ctx: ECDSA context
+ */
+__bpf_kfunc int bpf_ecdsa_maxsize(struct bpf_ecdsa_ctx *ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+
+ return crypto_sig_maxsize(ctx->tfm);
+}
+#endif /* CONFIG_CRYPTO_ECDSA */
+
__bpf_kfunc_end_defs();
BTF_KFUNCS_START(crypt_init_kfunc_btf_ids)
BTF_ID_FLAGS(func, bpf_crypto_ctx_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_crypto_ctx_release, KF_RELEASE)
BTF_ID_FLAGS(func, bpf_crypto_ctx_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
+#if IS_ENABLED(CONFIG_CRYPTO_ECDSA)
+BTF_ID_FLAGS(func, bpf_ecdsa_ctx_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_ecdsa_ctx_release, KF_RELEASE)
+BTF_ID_FLAGS(func, bpf_ecdsa_ctx_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
+#endif
BTF_KFUNCS_END(crypt_init_kfunc_btf_ids)
static const struct btf_kfunc_id_set crypt_init_kfunc_set = {
@@ -418,6 +628,12 @@ BTF_ID_FLAGS(func, bpf_crypto_encrypt, KF_RCU)
#if IS_ENABLED(CONFIG_CRYPTO_HASH2)
BTF_ID_FLAGS(func, bpf_crypto_hash, KF_RCU)
#endif
+#if IS_ENABLED(CONFIG_CRYPTO_ECDSA)
+BTF_ID_FLAGS(func, bpf_ecdsa_verify, 0)
+BTF_ID_FLAGS(func, bpf_ecdsa_keysize, 0)
+BTF_ID_FLAGS(func, bpf_ecdsa_digestsize, 0)
+BTF_ID_FLAGS(func, bpf_ecdsa_maxsize, 0)
+#endif
BTF_KFUNCS_END(crypt_kfunc_btf_ids)
static const struct btf_kfunc_id_set crypt_kfunc_set = {
@@ -428,6 +644,10 @@ static const struct btf_kfunc_id_set crypt_kfunc_set = {
BTF_ID_LIST(bpf_crypto_dtor_ids)
BTF_ID(struct, bpf_crypto_ctx)
BTF_ID(func, bpf_crypto_ctx_release)
+#if IS_ENABLED(CONFIG_CRYPTO_ECDSA)
+BTF_ID(struct, bpf_ecdsa_ctx)
+BTF_ID(func, bpf_ecdsa_ctx_release)
+#endif
static int __init crypto_kfunc_init(void)
{
@@ -437,6 +657,12 @@ static int __init crypto_kfunc_init(void)
.btf_id = bpf_crypto_dtor_ids[0],
.kfunc_btf_id = bpf_crypto_dtor_ids[1]
},
+#if IS_ENABLED(CONFIG_CRYPTO_ECDSA)
+ {
+ .btf_id = bpf_crypto_dtor_ids[2],
+ .kfunc_btf_id = bpf_crypto_dtor_ids[3]
+ },
+#endif
};
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &crypt_kfunc_set);
@@ -445,6 +671,10 @@ static int __init crypto_kfunc_init(void)
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &crypt_kfunc_set);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL,
&crypt_init_kfunc_set);
+ ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS,
+ &crypt_init_kfunc_set);
+ ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP,
+ &crypt_init_kfunc_set);
return ret ?: register_btf_id_dtor_kfuncs(bpf_crypto_dtors,
ARRAY_SIZE(bpf_crypto_dtors),
THIS_MODULE);
--
2.51.0
next prev parent reply other threads:[~2025-12-05 17:42 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-05 17:39 [PATCH bpf-next v2 0/5] Add cryptographic hash and signature verification kfuncs to BPF Daniel Hodges
2025-12-05 17:39 ` [PATCH bpf-next v2 1/5] crypto: Add BPF hash algorithm type registration module Daniel Hodges
2025-12-05 18:05 ` bot+bpf-ci
2025-12-06 1:00 ` Song Liu
2025-12-08 1:29 ` Daniel Hodges
2025-12-05 17:39 ` [PATCH bpf-next v2 2/5] bpf: Add SHA hash kfunc for cryptographic hashing Daniel Hodges
2025-12-05 18:50 ` Mykyta Yatsenko
2025-12-08 1:28 ` Daniel Hodges
2025-12-05 17:39 ` [PATCH bpf-next v2 3/5] selftests/bpf: Add tests for bpf_crypto_hash kfunc Daniel Hodges
2025-12-06 1:14 ` Song Liu
2025-12-05 17:39 ` Daniel Hodges [this message]
2025-12-05 17:39 ` [PATCH bpf-next v2 5/5] selftests/bpf: Add tests for ECDSA signature verification kfuncs Daniel Hodges
2025-12-06 1:16 ` Song Liu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251205173923.31740-5-git@danielhodges.dev \
--to=git@danielhodges.dev \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=eddyz87@gmail.com \
--cc=haoluo@google.com \
--cc=herbert@gondor.apana.org.au \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=kpsingh@kernel.org \
--cc=linux-crypto@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=sdf@fomichev.me \
--cc=shuah@kernel.org \
--cc=song@kernel.org \
--cc=vadim.fedorenko@linux.dev \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.