* [PATCH bpf-next v8 0/4] Add cryptographic hash and signature verification kfuncs to BPF
@ 2026-02-25 20:29 Daniel Hodges
2026-02-25 20:29 ` [PATCH bpf-next v8 1/4] bpf: Add hash kfunc for cryptographic hashing Daniel Hodges
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Daniel Hodges @ 2026-02-25 20:29 UTC (permalink / raw)
To: bpf, linux-crypto, linux-kernel, linux-kselftest
Cc: ast, andrii, daniel, vadim.fedorenko, song, yatsenko, martin.lau,
eddyz87, haoluo, jolsa, john.fastabend, kpsingh, sdf,
yonghong.song, herbert, davem, Daniel Hodges
This patch series enhances BPF's cryptographic functionality by introducing
kernel functions for SHA hashing and ECDSA signature verification. The changes
enable BPF programs to verify data integrity and authenticity across
networking, security, and observability use cases.
The series addresses two gaps in BPF's cryptographic toolkit:
1. Cryptographic hashing - supports content verification and message digest
preparation
2. Asymmetric signature verification - allows validation of signed data
without requiring private keys in the datapath
Use cases include:
- Verifying signed network packets or application data in XDP/TC programs
- Integrity checks within tracing and security monitoring
- Zero-trust security models with BPF-based credential verification
- Content-addressed storage in BPF-based filesystems
The implementation leverages existing BPF patterns: it uses bpf_dynptr for
memory safety, reuses kernel crypto libraries (lib/crypto/sha256.c and
crypto/ecdsa.c) rather than reimplementing algorithms, and provides
context-based APIs supporting multiple program types.
v1: https://lore.kernel.org/bpf/20251117211413.1394-1-git@danielhodges.dev/
v2: https://lore.kernel.org/bpf/20251205173923.31740-1-git@danielhodges.dev/
- Fixed redundant __bpf_dynptr_is_rdonly() checks (Vadim)
- Added BPF hash algorithm type registration module in crypto/ subsystem
- Added CONFIG_CRYPTO_HASH2 guards around bpf_crypto_hash() kfunc and its
BTF registration, matching the pattern used for CONFIG_CRYPTO_ECDSA
- Added mandatory digestsize validation for hash operations
v3: https://lore.kernel.org/bpf/20251208030117.18892-1-git@danielhodges.dev/
- Fixed patch ordering - header changes now in separate first commit before
crypto module to ensure bisectability (bot+bpf-ci)
- Fixed type mismatch - changed u32 to u64 for dynptr sizes in
bpf_crypto_hash() to match __bpf_dynptr_size() return type (Mykyta)
- Added CONFIG_CRYPTO_ECDSA to selftest config (Song)
- Refactored test code duplication with setup_skel() helper (Song)
- Added copyright notices to all new files
v4: https://lore.kernel.org/bpf/20260105173755.22515-1-git@danielhodges.dev/
- Reused common bpf_crypto_ctx structure for hash and signature operations
instead of separate context types (Song)
- Fixed integer truncation in bpf_crypto_hash when data_len > UINT_MAX
- Corrected KF_RCU flags for ECDSA kfuncs (only bpf_ecdsa_verify needs KF_RCU)
- Updated MAINTAINERS file in test patches
- Refactored selftests to use crypto_common.h for kfunc declarations
v5: https://lore.kernel.org/bpf/20260120184701.23082-1-git@danielhodges.dev/
- Fixed bisectability: moved bpf_crypto_type_id enum and type_id field
introduction to the hash module commit, before it's used by hash kfunc
- Renamed kfuncs from bpf_ecdsa_* to bpf_sig_* since signature verification
is not ECDSA-specific (Vadim)
- Added NULL checks in bpf_crypto_sig wrapper functions for optional
digest_size and max_size callbacks to prevent NULL pointer dereference
- Added extra validation in bpf_sig_digestsize/bpf_sig_maxsize kfuncs to
return -EOPNOTSUPP when underlying algorithm returns 0
- Renamed test files from ecdsa_verify to sig_verify for consistency
v6: https://lore.kernel.org/bpf/20260124174349.16861-1-git@danielhodges.dev/
- Fixed bisectability issue flagged by CI: squash hash module and hash kfunc
commits so NULL checks in bpf_crypto_ctx_create() are present before the
hash type is registered (bot+bpf-ci)
- Squash signature module and signature kfunc commits for the same reason
- This reduces the series from 7 to 4 commits while preserving all functionality
- Added type_id checks to bpf_crypto_encrypt() and bpf_crypto_decrypt() to
prevent NULL pointer dereference when called with non-skcipher contexts
v7: https://lore.kernel.org/bpf/20260202144749.22932-1-git@danielhodges.dev/
- Moved crypto/bpf_crypto_skcipher.c type_id assignment from signature patch
to hash patch for better logical organization
- Added missing CONFIG_CRYPTO_SIG, CONFIG_CRYPTO_SIG2, CONFIG_CRYPTO_ECDSA,
and CONFIG_CRYPTO_ECRDSA to selftest config to ensure signature verification
tests can build properly
v8:
- Fixed bpf_sig_keysize() documentation to correctly state return value is
in bits, not bytes, matching the underlying crypto_sig_keysize() API
(bot+bpf-ci)
Daniel Hodges (4):
bpf: Add hash kfunc for cryptographic hashing
selftests/bpf: Add tests for bpf_crypto_hash kfunc
bpf: Add signature verification kfuncs
selftests/bpf: Add tests for signature verification kfuncs
MAINTAINERS | 6 +
crypto/Makefile | 6 +
crypto/bpf_crypto_shash.c | 96 ++++++
crypto/bpf_crypto_sig.c | 89 ++++++
crypto/bpf_crypto_skcipher.c | 1 +
include/linux/bpf_crypto.h | 13 +
kernel/bpf/crypto.c | 210 ++++++++++++-
tools/testing/selftests/bpf/config | 6 +
.../selftests/bpf/prog_tests/crypto_hash.c | 210 +++++++++++++
.../selftests/bpf/prog_tests/sig_verify.c | 163 ++++++++++
.../selftests/bpf/progs/crypto_common.h | 8 +
.../testing/selftests/bpf/progs/crypto_hash.c | 231 ++++++++++++++
.../testing/selftests/bpf/progs/sig_verify.c | 286 ++++++++++++++++++
13 files changed, 1316 insertions(+), 9 deletions(-)
create mode 100644 crypto/bpf_crypto_shash.c
create mode 100644 crypto/bpf_crypto_sig.c
create mode 100644 tools/testing/selftests/bpf/prog_tests/crypto_hash.c
create mode 100644 tools/testing/selftests/bpf/prog_tests/sig_verify.c
create mode 100644 tools/testing/selftests/bpf/progs/crypto_hash.c
create mode 100644 tools/testing/selftests/bpf/progs/sig_verify.c
--
2.52.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH bpf-next v8 1/4] bpf: Add hash kfunc for cryptographic hashing
2026-02-25 20:29 [PATCH bpf-next v8 0/4] Add cryptographic hash and signature verification kfuncs to BPF Daniel Hodges
@ 2026-02-25 20:29 ` Daniel Hodges
2026-02-25 21:06 ` bot+bpf-ci
2026-02-25 20:29 ` [PATCH bpf-next v8 2/4] selftests/bpf: Add tests for bpf_crypto_hash kfunc Daniel Hodges
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: Daniel Hodges @ 2026-02-25 20:29 UTC (permalink / raw)
To: bpf, linux-crypto, linux-kernel, linux-kselftest
Cc: ast, andrii, daniel, vadim.fedorenko, song, yatsenko, martin.lau,
eddyz87, haoluo, jolsa, john.fastabend, kpsingh, sdf,
yonghong.song, herbert, davem, Daniel Hodges
Add bpf_crypto_shash module that registers a hash type with the BPF
crypto infrastructure, enabling BPF programs to access kernel hash
algorithms through a unified interface.
The bpf_crypto_type interface is extended with hash-specific callbacks:
- alloc_tfm: Allocates crypto_shash context with proper descriptor size
- free_tfm: Releases hash transform and context memory
- has_algo: Checks algorithm availability via crypto_has_shash()
- hash: Performs single-shot hashing via crypto_shash_digest()
- digestsize: Returns the output size for the hash algorithm
- get_flags: Exposes transform flags to BPF programs
Add bpf_crypto_hash() kfunc that works with any hash algorithm
registered in the kernel's crypto API through the BPF crypto type
system. This enables BPF programs to compute cryptographic hashes for
use cases such as content verification, integrity checking, and data
authentication.
Update bpf_crypto_ctx_create() to support keyless operations:
- Hash algorithms don't require keys, unlike ciphers
- Only validates key presence if type->setkey is defined
- Conditionally sets IV/state length for cipher operations only
Signed-off-by: Daniel Hodges <git@danielhodges.dev>
---
MAINTAINERS | 1 +
crypto/Makefile | 3 ++
crypto/bpf_crypto_shash.c | 96 ++++++++++++++++++++++++++++++++++++
crypto/bpf_crypto_skcipher.c | 1 +
include/linux/bpf_crypto.h | 9 ++++
kernel/bpf/crypto.c | 93 ++++++++++++++++++++++++++++++----
6 files changed, 194 insertions(+), 9 deletions(-)
create mode 100644 crypto/bpf_crypto_shash.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 55af015174a5..6c78aae1bc6c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4801,6 +4801,7 @@ BPF [CRYPTO]
M: Vadim Fedorenko <vadim.fedorenko@linux.dev>
L: bpf@vger.kernel.org
S: Maintained
+F: crypto/bpf_crypto_shash.c
F: crypto/bpf_crypto_skcipher.c
F: include/linux/bpf_crypto.h
F: kernel/bpf/crypto.c
diff --git a/crypto/Makefile b/crypto/Makefile
index 04e269117589..4db6a76d4c3e 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -32,6 +32,9 @@ obj-$(CONFIG_CRYPTO_ECHAINIV) += echainiv.o
crypto_hash-y += ahash.o
crypto_hash-y += shash.o
obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o
+ifeq ($(CONFIG_BPF_SYSCALL),y)
+obj-$(CONFIG_CRYPTO_HASH2) += bpf_crypto_shash.o
+endif
obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o
obj-$(CONFIG_CRYPTO_SIG2) += sig.o
diff --git a/crypto/bpf_crypto_shash.c b/crypto/bpf_crypto_shash.c
new file mode 100644
index 000000000000..6e9b0d757ec9
--- /dev/null
+++ b/crypto/bpf_crypto_shash.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/bpf_crypto.h>
+#include <crypto/hash.h>
+
+struct bpf_shash_ctx {
+ struct crypto_shash *tfm;
+ struct shash_desc desc;
+};
+
+static void *bpf_crypto_shash_alloc_tfm(const char *algo)
+{
+ struct bpf_shash_ctx *ctx;
+ struct crypto_shash *tfm;
+
+ tfm = crypto_alloc_shash(algo, 0, 0);
+ if (IS_ERR(tfm))
+ return tfm;
+
+ ctx = kzalloc(sizeof(*ctx) + crypto_shash_descsize(tfm), GFP_KERNEL);
+ if (!ctx) {
+ crypto_free_shash(tfm);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ctx->tfm = tfm;
+ ctx->desc.tfm = tfm;
+
+ return ctx;
+}
+
+static void bpf_crypto_shash_free_tfm(void *tfm)
+{
+ struct bpf_shash_ctx *ctx = tfm;
+
+ crypto_free_shash(ctx->tfm);
+ kfree(ctx);
+}
+
+static int bpf_crypto_shash_has_algo(const char *algo)
+{
+ return crypto_has_shash(algo, 0, 0);
+}
+
+static int bpf_crypto_shash_hash(void *tfm, const u8 *data, u8 *out,
+ unsigned int len)
+{
+ struct bpf_shash_ctx *ctx = tfm;
+
+ return crypto_shash_digest(&ctx->desc, data, len, out);
+}
+
+static unsigned int bpf_crypto_shash_digestsize(void *tfm)
+{
+ struct bpf_shash_ctx *ctx = tfm;
+
+ return crypto_shash_digestsize(ctx->tfm);
+}
+
+static u32 bpf_crypto_shash_get_flags(void *tfm)
+{
+ struct bpf_shash_ctx *ctx = tfm;
+
+ return crypto_shash_get_flags(ctx->tfm);
+}
+
+static const struct bpf_crypto_type bpf_crypto_shash_type = {
+ .alloc_tfm = bpf_crypto_shash_alloc_tfm,
+ .free_tfm = bpf_crypto_shash_free_tfm,
+ .has_algo = bpf_crypto_shash_has_algo,
+ .hash = bpf_crypto_shash_hash,
+ .digestsize = bpf_crypto_shash_digestsize,
+ .get_flags = bpf_crypto_shash_get_flags,
+ .owner = THIS_MODULE,
+ .type_id = BPF_CRYPTO_TYPE_HASH,
+ .name = "hash",
+};
+
+static int __init bpf_crypto_shash_init(void)
+{
+ return bpf_crypto_register_type(&bpf_crypto_shash_type);
+}
+
+static void __exit bpf_crypto_shash_exit(void)
+{
+ int err = bpf_crypto_unregister_type(&bpf_crypto_shash_type);
+
+ WARN_ON_ONCE(err);
+}
+
+module_init(bpf_crypto_shash_init);
+module_exit(bpf_crypto_shash_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Hash algorithm support for BPF");
diff --git a/crypto/bpf_crypto_skcipher.c b/crypto/bpf_crypto_skcipher.c
index a88798d3e8c8..79d310fbcc48 100644
--- a/crypto/bpf_crypto_skcipher.c
+++ b/crypto/bpf_crypto_skcipher.c
@@ -63,6 +63,7 @@ static const struct bpf_crypto_type bpf_crypto_lskcipher_type = {
.statesize = bpf_crypto_lskcipher_statesize,
.get_flags = bpf_crypto_lskcipher_get_flags,
.owner = THIS_MODULE,
+ .type_id = BPF_CRYPTO_TYPE_SKCIPHER,
.name = "skcipher",
};
diff --git a/include/linux/bpf_crypto.h b/include/linux/bpf_crypto.h
index a41e71d4e2d9..cf2c66f9782b 100644
--- a/include/linux/bpf_crypto.h
+++ b/include/linux/bpf_crypto.h
@@ -3,6 +3,12 @@
#ifndef _BPF_CRYPTO_H
#define _BPF_CRYPTO_H
+enum bpf_crypto_type_id {
+ BPF_CRYPTO_TYPE_SKCIPHER = 1,
+ BPF_CRYPTO_TYPE_HASH,
+ BPF_CRYPTO_TYPE_SIG,
+};
+
struct bpf_crypto_type {
void *(*alloc_tfm)(const char *algo);
void (*free_tfm)(void *tfm);
@@ -11,10 +17,13 @@ struct bpf_crypto_type {
int (*setauthsize)(void *tfm, unsigned int authsize);
int (*encrypt)(void *tfm, const u8 *src, u8 *dst, unsigned int len, u8 *iv);
int (*decrypt)(void *tfm, const u8 *src, u8 *dst, unsigned int len, u8 *iv);
+ int (*hash)(void *tfm, const u8 *data, u8 *out, unsigned int len);
unsigned int (*ivsize)(void *tfm);
unsigned int (*statesize)(void *tfm);
+ unsigned int (*digestsize)(void *tfm);
u32 (*get_flags)(void *tfm);
struct module *owner;
+ enum bpf_crypto_type_id type_id;
char name[14];
};
diff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c
index 51f89cecefb4..3b2b0d06f1b2 100644
--- a/kernel/bpf/crypto.c
+++ b/kernel/bpf/crypto.c
@@ -139,7 +139,7 @@ __bpf_kfunc_start_defs();
* It may return NULL if no memory is available.
* @params: pointer to struct bpf_crypto_params which contains all the
* details needed to initialise crypto context.
- * @params__sz: size of steuct bpf_crypto_params usef by bpf program
+ * @params__sz: size of struct bpf_crypto_params used by bpf program
* @err: integer to store error code when NULL is returned.
*/
__bpf_kfunc struct bpf_crypto_ctx *
@@ -171,7 +171,12 @@ bpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,
goto err_module_put;
}
- if (!params->key_len || params->key_len > sizeof(params->key)) {
+ /* Hash operations don't require a key, but cipher operations do */
+ if (params->key_len > sizeof(params->key)) {
+ *err = -EINVAL;
+ goto err_module_put;
+ }
+ if (!params->key_len && type->setkey) {
*err = -EINVAL;
goto err_module_put;
}
@@ -195,16 +200,23 @@ bpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,
goto err_free_tfm;
}
- *err = type->setkey(ctx->tfm, params->key, params->key_len);
- if (*err)
- goto err_free_tfm;
+ if (params->key_len) {
+ if (!type->setkey) {
+ *err = -EINVAL;
+ goto err_free_tfm;
+ }
+ *err = type->setkey(ctx->tfm, params->key, params->key_len);
+ if (*err)
+ goto err_free_tfm;
- if (type->get_flags(ctx->tfm) & CRYPTO_TFM_NEED_KEY) {
- *err = -EINVAL;
- goto err_free_tfm;
+ if (type->get_flags(ctx->tfm) & CRYPTO_TFM_NEED_KEY) {
+ *err = -EINVAL;
+ goto err_free_tfm;
+ }
}
- ctx->siv_len = type->ivsize(ctx->tfm) + type->statesize(ctx->tfm);
+ if (type->ivsize && type->statesize)
+ ctx->siv_len = type->ivsize(ctx->tfm) + type->statesize(ctx->tfm);
refcount_set(&ctx->usage, 1);
@@ -325,6 +337,9 @@ __bpf_kfunc int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx,
const struct bpf_dynptr_kern *dst_kern = (struct bpf_dynptr_kern *)dst;
const struct bpf_dynptr_kern *siv_kern = (struct bpf_dynptr_kern *)siv__nullable;
+ if (ctx->type->type_id != BPF_CRYPTO_TYPE_SKCIPHER)
+ return -EINVAL;
+
return bpf_crypto_crypt(ctx, src_kern, dst_kern, siv_kern, true);
}
@@ -346,9 +361,64 @@ __bpf_kfunc int bpf_crypto_encrypt(struct bpf_crypto_ctx *ctx,
const struct bpf_dynptr_kern *dst_kern = (struct bpf_dynptr_kern *)dst;
const struct bpf_dynptr_kern *siv_kern = (struct bpf_dynptr_kern *)siv__nullable;
+ if (ctx->type->type_id != BPF_CRYPTO_TYPE_SKCIPHER)
+ return -EINVAL;
+
return bpf_crypto_crypt(ctx, src_kern, dst_kern, siv_kern, false);
}
+#if IS_ENABLED(CONFIG_CRYPTO_HASH2)
+/**
+ * bpf_crypto_hash() - Compute hash using configured context
+ * @ctx: The crypto context being used. The ctx must be a trusted pointer.
+ * @data: bpf_dynptr to the input data to hash. Must be a trusted pointer.
+ * @out: bpf_dynptr to the output buffer. Must be a trusted pointer.
+ *
+ * Computes hash of the input data using the crypto context. The output buffer
+ * must be at least as large as the digest size of the hash algorithm.
+ */
+__bpf_kfunc int bpf_crypto_hash(struct bpf_crypto_ctx *ctx,
+ const struct bpf_dynptr *data,
+ const struct bpf_dynptr *out)
+{
+ const struct bpf_dynptr_kern *data_kern = (struct bpf_dynptr_kern *)data;
+ const struct bpf_dynptr_kern *out_kern = (struct bpf_dynptr_kern *)out;
+ unsigned int digestsize;
+ u64 data_len, out_len;
+ const u8 *data_ptr;
+ u8 *out_ptr;
+
+ if (ctx->type->type_id != BPF_CRYPTO_TYPE_HASH)
+ return -EINVAL;
+
+ if (!ctx->type->hash)
+ return -EOPNOTSUPP;
+
+ data_len = __bpf_dynptr_size(data_kern);
+ out_len = __bpf_dynptr_size(out_kern);
+
+ if (data_len == 0 || data_len > UINT_MAX)
+ return -EINVAL;
+
+ if (!ctx->type->digestsize)
+ return -EOPNOTSUPP;
+
+ digestsize = ctx->type->digestsize(ctx->tfm);
+ if (out_len < digestsize)
+ return -EINVAL;
+
+ data_ptr = __bpf_dynptr_data(data_kern, data_len);
+ if (!data_ptr)
+ return -EINVAL;
+
+ out_ptr = __bpf_dynptr_data_rw(out_kern, out_len);
+ if (!out_ptr)
+ return -EINVAL;
+
+ return ctx->type->hash(ctx->tfm, data_ptr, out_ptr, data_len);
+}
+#endif /* CONFIG_CRYPTO_HASH2 */
+
__bpf_kfunc_end_defs();
BTF_KFUNCS_START(crypt_init_kfunc_btf_ids)
@@ -365,6 +435,9 @@ static const struct btf_kfunc_id_set crypt_init_kfunc_set = {
BTF_KFUNCS_START(crypt_kfunc_btf_ids)
BTF_ID_FLAGS(func, bpf_crypto_decrypt, KF_RCU)
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
BTF_KFUNCS_END(crypt_kfunc_btf_ids)
static const struct btf_kfunc_id_set crypt_kfunc_set = {
@@ -389,6 +462,8 @@ static int __init crypto_kfunc_init(void)
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &crypt_kfunc_set);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_ACT, &crypt_kfunc_set);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &crypt_kfunc_set);
+ /* Register for SYSCALL programs to enable testing and debugging */
+ 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);
return ret ?: register_btf_id_dtor_kfuncs(bpf_crypto_dtors,
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH bpf-next v8 2/4] selftests/bpf: Add tests for bpf_crypto_hash kfunc
2026-02-25 20:29 [PATCH bpf-next v8 0/4] Add cryptographic hash and signature verification kfuncs to BPF Daniel Hodges
2026-02-25 20:29 ` [PATCH bpf-next v8 1/4] bpf: Add hash kfunc for cryptographic hashing Daniel Hodges
@ 2026-02-25 20:29 ` Daniel Hodges
2026-02-25 20:29 ` [PATCH bpf-next v8 3/4] bpf: Add signature verification kfuncs Daniel Hodges
2026-02-25 20:29 ` [PATCH bpf-next v8 4/4] selftests/bpf: Add tests for " Daniel Hodges
3 siblings, 0 replies; 7+ messages in thread
From: Daniel Hodges @ 2026-02-25 20:29 UTC (permalink / raw)
To: bpf, linux-crypto, linux-kernel, linux-kselftest
Cc: ast, andrii, daniel, vadim.fedorenko, song, yatsenko, martin.lau,
eddyz87, haoluo, jolsa, john.fastabend, kpsingh, sdf,
yonghong.song, herbert, davem, Daniel Hodges
Add selftests to validate the bpf_crypto_hash works properly. The tests
verify both correct functionality and proper error handling.
Test Data:
All tests use the well-known NIST test vector input "abc" and validate
against the standardized expected outputs for each algorithm. This ensures
the BPF kfunc wrappers correctly delegate to the kernel crypto library.
Signed-off-by: Daniel Hodges <git@danielhodges.dev>
---
MAINTAINERS | 2 +
tools/testing/selftests/bpf/config | 2 +
.../selftests/bpf/prog_tests/crypto_hash.c | 210 ++++++++++++++++
.../selftests/bpf/progs/crypto_common.h | 2 +
.../testing/selftests/bpf/progs/crypto_hash.c | 231 ++++++++++++++++++
5 files changed, 447 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/crypto_hash.c
create mode 100644 tools/testing/selftests/bpf/progs/crypto_hash.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 6c78aae1bc6c..fd72648719a7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4805,6 +4805,8 @@ F: crypto/bpf_crypto_shash.c
F: crypto/bpf_crypto_skcipher.c
F: include/linux/bpf_crypto.h
F: kernel/bpf/crypto.c
+F: tools/testing/selftests/bpf/prog_tests/crypto_hash.c
+F: tools/testing/selftests/bpf/progs/crypto_hash.c
BPF [DOCUMENTATION] (Related to Standardization)
R: David Vernet <void@manifault.com>
diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index 24855381290d..dcfdca7daea3 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -13,6 +13,8 @@ CONFIG_BPF_SYSCALL=y
CONFIG_CGROUP_BPF=y
CONFIG_CRYPTO_HMAC=y
CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_SHA512=y
+CONFIG_CRYPTO_HASH2=y
CONFIG_CRYPTO_USER_API=y
CONFIG_CRYPTO_USER_API_HASH=y
CONFIG_CRYPTO_USER_API_SKCIPHER=y
diff --git a/tools/testing/selftests/bpf/prog_tests/crypto_hash.c b/tools/testing/selftests/bpf/prog_tests/crypto_hash.c
new file mode 100644
index 000000000000..0c78b5f46c9c
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/crypto_hash.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+
+#include <test_progs.h>
+#include <errno.h>
+#include "crypto_hash.skel.h"
+
+/* NIST test vectors for SHA-256("abc") */
+static const unsigned char expected_sha256[32] = {
+ 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+ 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
+};
+
+/* NIST test vectors for SHA-384("abc") */
+static const unsigned char expected_sha384[48] = {
+ 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b,
+ 0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07,
+ 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63,
+ 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed,
+ 0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23,
+ 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7
+};
+
+/* NIST test vectors for SHA-512("abc") */
+static const unsigned char expected_sha512[64] = {
+ 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
+ 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
+ 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+ 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
+ 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
+ 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+ 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
+ 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f
+};
+
+static struct crypto_hash *setup_skel(void)
+{
+ struct crypto_hash *skel;
+
+ skel = crypto_hash__open_and_load();
+ if (!skel) {
+ /* Skip if kfuncs not available (CONFIG_CRYPTO_HASH2 not set) */
+ if (errno == ENOENT || errno == EINVAL) {
+ test__skip();
+ return NULL;
+ }
+ ASSERT_OK_PTR(skel, "crypto_hash__open_and_load");
+ return NULL;
+ }
+
+ return skel;
+}
+
+static void test_sha256_basic(void)
+{
+ struct crypto_hash *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = setup_skel();
+ if (!skel)
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_sha256);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_sha256");
+ ASSERT_EQ(skel->data->sha256_status, 0, "sha256_status");
+ ASSERT_EQ(memcmp(skel->bss->sha256_output, expected_sha256, 32), 0,
+ "sha256_output_match");
+
+ crypto_hash__destroy(skel);
+}
+
+static void test_sha384_basic(void)
+{
+ struct crypto_hash *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = setup_skel();
+ if (!skel)
+ return;
+ prog_fd = bpf_program__fd(skel->progs.test_sha384);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_sha384");
+ ASSERT_EQ(skel->data->sha384_status, 0, "sha384_status");
+ ASSERT_EQ(memcmp(skel->bss->sha384_output, expected_sha384, 48), 0,
+ "sha384_output_match");
+
+ crypto_hash__destroy(skel);
+}
+
+static void test_sha512_basic(void)
+{
+ struct crypto_hash *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = setup_skel();
+ if (!skel)
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_sha512);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_sha512");
+ ASSERT_EQ(skel->data->sha512_status, 0, "sha512_status");
+ ASSERT_EQ(memcmp(skel->bss->sha512_output, expected_sha512, 64), 0,
+ "sha512_output_match");
+
+ crypto_hash__destroy(skel);
+}
+
+static void test_sha256_invalid_params(void)
+{
+ struct crypto_hash *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = setup_skel();
+ if (!skel)
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_sha256_zero_len);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_zero_len");
+ ASSERT_EQ(skel->data->sha256_status, 0, "zero_len_rejected");
+
+ crypto_hash__destroy(skel);
+}
+
+static void test_hash_with_key_rejected(void)
+{
+ struct crypto_hash *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = setup_skel();
+ if (!skel)
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_hash_with_key_rejected);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_hash_with_key_rejected");
+ ASSERT_EQ(skel->data->hash_with_key_status, 0, "hash_with_key_rejected");
+
+ crypto_hash__destroy(skel);
+}
+
+static void test_hash_output_too_small(void)
+{
+ struct crypto_hash *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = setup_skel();
+ if (!skel)
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_hash_output_too_small);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_hash_output_too_small");
+ ASSERT_EQ(skel->data->hash_output_too_small_status, 0, "hash_output_too_small");
+
+ crypto_hash__destroy(skel);
+}
+
+static void test_hash_on_skcipher_ctx(void)
+{
+ struct crypto_hash *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = setup_skel();
+ if (!skel)
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_hash_on_skcipher_ctx);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_hash_on_skcipher_ctx");
+ ASSERT_EQ(skel->data->hash_on_skcipher_status, 0, "hash_on_skcipher_rejected");
+
+ crypto_hash__destroy(skel);
+}
+
+void test_crypto_hash(void)
+{
+ if (test__start_subtest("sha256_basic"))
+ test_sha256_basic();
+ if (test__start_subtest("sha384_basic"))
+ test_sha384_basic();
+ if (test__start_subtest("sha512_basic"))
+ test_sha512_basic();
+ if (test__start_subtest("sha256_invalid_params"))
+ test_sha256_invalid_params();
+ if (test__start_subtest("hash_with_key_rejected"))
+ test_hash_with_key_rejected();
+ if (test__start_subtest("hash_output_too_small"))
+ test_hash_output_too_small();
+ if (test__start_subtest("hash_on_skcipher_ctx"))
+ test_hash_on_skcipher_ctx();
+}
diff --git a/tools/testing/selftests/bpf/progs/crypto_common.h b/tools/testing/selftests/bpf/progs/crypto_common.h
index 57dd7a68a8c3..2f04f08f890b 100644
--- a/tools/testing/selftests/bpf/progs/crypto_common.h
+++ b/tools/testing/selftests/bpf/progs/crypto_common.h
@@ -15,6 +15,8 @@ int bpf_crypto_encrypt(struct bpf_crypto_ctx *ctx, const struct bpf_dynptr *src,
const struct bpf_dynptr *dst, const struct bpf_dynptr *iv) __ksym;
int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx, const struct bpf_dynptr *src,
const struct bpf_dynptr *dst, const struct bpf_dynptr *iv) __ksym;
+int bpf_crypto_hash(struct bpf_crypto_ctx *ctx, const struct bpf_dynptr *data,
+ const struct bpf_dynptr *out) __ksym;
struct __crypto_ctx_value {
struct bpf_crypto_ctx __kptr * ctx;
diff --git a/tools/testing/selftests/bpf/progs/crypto_hash.c b/tools/testing/selftests/bpf/progs/crypto_hash.c
new file mode 100644
index 000000000000..0258fdbe3e71
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/crypto_hash.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+#include "bpf_kfuncs.h"
+#include "crypto_common.h"
+
+unsigned char test_input[3] = "abc";
+
+/* Expected SHA-256 hash of "abc" */
+/* ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad */
+unsigned char expected_sha256[32] = {
+ 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+ 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
+};
+
+/* Output buffers for test results */
+unsigned char sha256_output[32] = {};
+unsigned char sha384_output[48] = {};
+unsigned char sha512_output[64] = {};
+unsigned char small_output[16] = {}; /* Intentionally small for output_too_small test */
+
+int sha256_status = -1;
+int sha384_status = -1;
+int sha512_status = -1;
+int hash_with_key_status = -1;
+int hash_output_too_small_status = -1;
+int hash_on_skcipher_status = -1;
+
+SEC("syscall")
+int test_sha256(void *ctx)
+{
+ struct bpf_dynptr input_ptr, output_ptr;
+ struct bpf_crypto_ctx *hash_ctx;
+ struct bpf_crypto_params params = {
+ .type = "hash",
+ .algo = "sha256",
+ .key_len = 0,
+ };
+ int err = 0;
+
+ hash_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!hash_ctx) {
+ sha256_status = err;
+ return 0;
+ }
+
+ bpf_dynptr_from_mem(test_input, sizeof(test_input), 0, &input_ptr);
+ bpf_dynptr_from_mem(sha256_output, sizeof(sha256_output), 0, &output_ptr);
+
+ sha256_status = bpf_crypto_hash(hash_ctx, &input_ptr, &output_ptr);
+ bpf_crypto_ctx_release(hash_ctx);
+ return 0;
+}
+
+SEC("syscall")
+int test_sha384(void *ctx)
+{
+ struct bpf_dynptr input_ptr, output_ptr;
+ struct bpf_crypto_ctx *hash_ctx;
+ struct bpf_crypto_params params = {
+ .type = "hash",
+ .algo = "sha384",
+ .key_len = 0,
+ };
+ int err = 0;
+
+ hash_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!hash_ctx) {
+ sha384_status = err;
+ return 0;
+ }
+
+ bpf_dynptr_from_mem(test_input, sizeof(test_input), 0, &input_ptr);
+ bpf_dynptr_from_mem(sha384_output, sizeof(sha384_output), 0, &output_ptr);
+
+ sha384_status = bpf_crypto_hash(hash_ctx, &input_ptr, &output_ptr);
+ bpf_crypto_ctx_release(hash_ctx);
+ return 0;
+}
+
+SEC("syscall")
+int test_sha512(void *ctx)
+{
+ struct bpf_dynptr input_ptr, output_ptr;
+ struct bpf_crypto_ctx *hash_ctx;
+ struct bpf_crypto_params params = {
+ .type = "hash",
+ .algo = "sha512",
+ .key_len = 0,
+ };
+ int err = 0;
+
+ hash_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!hash_ctx) {
+ sha512_status = err;
+ return 0;
+ }
+
+ bpf_dynptr_from_mem(test_input, sizeof(test_input), 0, &input_ptr);
+ bpf_dynptr_from_mem(sha512_output, sizeof(sha512_output), 0, &output_ptr);
+
+ sha512_status = bpf_crypto_hash(hash_ctx, &input_ptr, &output_ptr);
+ bpf_crypto_ctx_release(hash_ctx);
+ return 0;
+}
+
+SEC("syscall")
+int test_sha256_zero_len(void *ctx)
+{
+ struct bpf_dynptr input_ptr, output_ptr;
+ struct bpf_crypto_ctx *hash_ctx;
+ struct bpf_crypto_params params = {
+ .type = "hash",
+ .algo = "sha256",
+ .key_len = 0,
+ };
+ int err = 0;
+ int ret;
+
+ hash_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!hash_ctx) {
+ sha256_status = err;
+ return 0;
+ }
+
+ bpf_dynptr_from_mem(test_input, 0, 0, &input_ptr);
+ bpf_dynptr_from_mem(sha256_output, sizeof(sha256_output), 0, &output_ptr);
+
+ ret = bpf_crypto_hash(hash_ctx, &input_ptr, &output_ptr);
+ sha256_status = (ret == -22) ? 0 : ret;
+ bpf_crypto_ctx_release(hash_ctx);
+ return 0;
+}
+
+SEC("syscall")
+int test_hash_with_key_rejected(void *ctx)
+{
+ struct bpf_crypto_ctx *hash_ctx;
+ struct bpf_crypto_params params = {
+ .type = "hash",
+ .algo = "sha256",
+ .key_len = 16, /* Hash algorithms don't support keys */
+ };
+ int err = 0;
+
+ /* Set some dummy key data */
+ params.key[0] = 0x01;
+ params.key[1] = 0x02;
+
+ hash_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!hash_ctx) {
+ /* Expected: should fail with -EINVAL (-22) */
+ hash_with_key_status = (err == -22) ? 0 : err;
+ return 0;
+ }
+
+ /* Should not reach here - context creation should have failed */
+ hash_with_key_status = -1;
+ bpf_crypto_ctx_release(hash_ctx);
+ return 0;
+}
+
+SEC("syscall")
+int test_hash_output_too_small(void *ctx)
+{
+ struct bpf_dynptr input_ptr, output_ptr;
+ struct bpf_crypto_ctx *hash_ctx;
+ struct bpf_crypto_params params = {
+ .type = "hash",
+ .algo = "sha256",
+ .key_len = 0,
+ };
+ int err = 0;
+ int ret;
+
+ hash_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!hash_ctx) {
+ hash_output_too_small_status = err;
+ return 0;
+ }
+
+ bpf_dynptr_from_mem(test_input, sizeof(test_input), 0, &input_ptr);
+ bpf_dynptr_from_mem(small_output, sizeof(small_output), 0, &output_ptr);
+
+ ret = bpf_crypto_hash(hash_ctx, &input_ptr, &output_ptr);
+ /* Expected: should fail with -EINVAL (-22) */
+ hash_output_too_small_status = (ret == -22) ? 0 : ret;
+ bpf_crypto_ctx_release(hash_ctx);
+ return 0;
+}
+
+SEC("syscall")
+int test_hash_on_skcipher_ctx(void *ctx)
+{
+ struct bpf_dynptr input_ptr, output_ptr;
+ struct bpf_crypto_ctx *cipher_ctx;
+ struct bpf_crypto_params params = {
+ .type = "skcipher",
+ .algo = "ecb(aes)",
+ .key_len = 16,
+ };
+ int err = 0;
+ int ret;
+
+ params.key[0] = 0x00; params.key[1] = 0x01; params.key[2] = 0x02; params.key[3] = 0x03;
+ params.key[4] = 0x04; params.key[5] = 0x05; params.key[6] = 0x06; params.key[7] = 0x07;
+ params.key[8] = 0x08; params.key[9] = 0x09; params.key[10] = 0x0a; params.key[11] = 0x0b;
+ params.key[12] = 0x0c; params.key[13] = 0x0d; params.key[14] = 0x0e; params.key[15] = 0x0f;
+
+ cipher_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!cipher_ctx) {
+ hash_on_skcipher_status = err;
+ return 0;
+ }
+
+ bpf_dynptr_from_mem(test_input, sizeof(test_input), 0, &input_ptr);
+ bpf_dynptr_from_mem(sha256_output, sizeof(sha256_output), 0, &output_ptr);
+
+ ret = bpf_crypto_hash(cipher_ctx, &input_ptr, &output_ptr);
+ /* Expected: should fail with -EINVAL (-22) due to type_id mismatch */
+ hash_on_skcipher_status = (ret == -22) ? 0 : ret;
+ bpf_crypto_ctx_release(cipher_ctx);
+ return 0;
+}
+
+char __license[] SEC("license") = "GPL";
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH bpf-next v8 3/4] bpf: Add signature verification kfuncs
2026-02-25 20:29 [PATCH bpf-next v8 0/4] Add cryptographic hash and signature verification kfuncs to BPF Daniel Hodges
2026-02-25 20:29 ` [PATCH bpf-next v8 1/4] bpf: Add hash kfunc for cryptographic hashing Daniel Hodges
2026-02-25 20:29 ` [PATCH bpf-next v8 2/4] selftests/bpf: Add tests for bpf_crypto_hash kfunc Daniel Hodges
@ 2026-02-25 20:29 ` Daniel Hodges
2026-02-25 21:06 ` bot+bpf-ci
2026-02-25 20:29 ` [PATCH bpf-next v8 4/4] selftests/bpf: Add tests for " Daniel Hodges
3 siblings, 1 reply; 7+ messages in thread
From: Daniel Hodges @ 2026-02-25 20:29 UTC (permalink / raw)
To: bpf, linux-crypto, linux-kernel, linux-kselftest
Cc: ast, andrii, daniel, vadim.fedorenko, song, yatsenko, martin.lau,
eddyz87, haoluo, jolsa, john.fastabend, kpsingh, sdf,
yonghong.song, herbert, davem, Daniel Hodges
Add a bpf_crypto_sig module that registers signature verification
algorithms with the BPF crypto type system. This enables signature
operations (like ECDSA) to use the unified bpf_crypto_ctx structure.
The module provides:
- alloc_tfm/free_tfm for crypto_sig transform lifecycle
- has_algo to check algorithm availability
- setkey for public key configuration
- verify for signature verification
- get_flags for crypto API flags
Introduce bpf_sig_verify, bpf_sig_keysize, bpf_sig_digestsize,
and bpf_sig_maxsize kfuncs enabling BPF programs to verify digital
signatures using the kernel's crypto infrastructure.
Add enum bpf_crypto_type_id for runtime type checking to ensure
operations are performed on the correct crypto context type. The enum
values are assigned to all crypto type modules (skcipher, hash, sig).
The verify kfunc takes a crypto context (initialized with the sig
type and appropriate algorithm like "ecdsa-nist-p256"), a message
digest, and a signature. These kfuncs support any signature algorithm
registered with the crypto subsystem (e.g., ECDSA, RSA).
Signed-off-by: Daniel Hodges <git@danielhodges.dev>
---
MAINTAINERS | 1 +
crypto/Makefile | 3 +
crypto/bpf_crypto_sig.c | 89 ++++++++++++++++++++++++++++
include/linux/bpf_crypto.h | 4 ++
kernel/bpf/crypto.c | 117 +++++++++++++++++++++++++++++++++++++
5 files changed, 214 insertions(+)
create mode 100644 crypto/bpf_crypto_sig.c
diff --git a/MAINTAINERS b/MAINTAINERS
index fd72648719a7..2d7d4bc3e7b3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4802,6 +4802,7 @@ M: Vadim Fedorenko <vadim.fedorenko@linux.dev>
L: bpf@vger.kernel.org
S: Maintained
F: crypto/bpf_crypto_shash.c
+F: crypto/bpf_crypto_sig.c
F: crypto/bpf_crypto_skcipher.c
F: include/linux/bpf_crypto.h
F: kernel/bpf/crypto.c
diff --git a/crypto/Makefile b/crypto/Makefile
index 4db6a76d4c3e..b75963692bc2 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -38,6 +38,9 @@ endif
obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o
obj-$(CONFIG_CRYPTO_SIG2) += sig.o
+ifeq ($(CONFIG_BPF_SYSCALL),y)
+obj-$(CONFIG_CRYPTO_SIG2) += bpf_crypto_sig.o
+endif
obj-$(CONFIG_CRYPTO_KPP2) += kpp.o
obj-$(CONFIG_CRYPTO_HKDF) += hkdf.o
diff --git a/crypto/bpf_crypto_sig.c b/crypto/bpf_crypto_sig.c
new file mode 100644
index 000000000000..2dc82c5f9abb
--- /dev/null
+++ b/crypto/bpf_crypto_sig.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/bpf_crypto.h>
+#include <linux/crypto.h>
+#include <crypto/sig.h>
+
+static void *bpf_crypto_sig_alloc_tfm(const char *algo)
+{
+ return crypto_alloc_sig(algo, 0, 0);
+}
+
+static void bpf_crypto_sig_free_tfm(void *tfm)
+{
+ crypto_free_sig(tfm);
+}
+
+static int bpf_crypto_sig_has_algo(const char *algo)
+{
+ return crypto_has_alg(algo, CRYPTO_ALG_TYPE_SIG, CRYPTO_ALG_TYPE_MASK);
+}
+
+static u32 bpf_crypto_sig_get_flags(void *tfm)
+{
+ return crypto_tfm_get_flags(crypto_sig_tfm(tfm));
+}
+
+static int bpf_crypto_sig_setkey(void *tfm, const u8 *key, unsigned int keylen)
+{
+ return crypto_sig_set_pubkey(tfm, key, keylen);
+}
+
+static int bpf_crypto_sig_verify(void *tfm, const u8 *sig, unsigned int sig_len,
+ const u8 *msg, unsigned int msg_len)
+{
+ return crypto_sig_verify(tfm, sig, sig_len, msg, msg_len);
+}
+
+static unsigned int bpf_crypto_sig_keysize(void *tfm)
+{
+ return crypto_sig_keysize(tfm);
+}
+
+static unsigned int bpf_crypto_sig_digestsize(void *tfm)
+{
+ struct sig_alg *alg = crypto_sig_alg(tfm);
+
+ return alg->digest_size ? alg->digest_size(tfm) : 0;
+}
+
+static unsigned int bpf_crypto_sig_maxsize(void *tfm)
+{
+ struct sig_alg *alg = crypto_sig_alg(tfm);
+
+ return alg->max_size ? alg->max_size(tfm) : 0;
+}
+
+static const struct bpf_crypto_type bpf_crypto_sig_type = {
+ .alloc_tfm = bpf_crypto_sig_alloc_tfm,
+ .free_tfm = bpf_crypto_sig_free_tfm,
+ .has_algo = bpf_crypto_sig_has_algo,
+ .get_flags = bpf_crypto_sig_get_flags,
+ .setkey = bpf_crypto_sig_setkey,
+ .verify = bpf_crypto_sig_verify,
+ .keysize = bpf_crypto_sig_keysize,
+ .digestsize = bpf_crypto_sig_digestsize,
+ .maxsize = bpf_crypto_sig_maxsize,
+ .owner = THIS_MODULE,
+ .type_id = BPF_CRYPTO_TYPE_SIG,
+ .name = "sig",
+};
+
+static int __init bpf_crypto_sig_init(void)
+{
+ return bpf_crypto_register_type(&bpf_crypto_sig_type);
+}
+
+static void __exit bpf_crypto_sig_exit(void)
+{
+ int err = bpf_crypto_unregister_type(&bpf_crypto_sig_type);
+
+ WARN_ON_ONCE(err);
+}
+
+module_init(bpf_crypto_sig_init);
+module_exit(bpf_crypto_sig_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Signature algorithm support for BPF");
diff --git a/include/linux/bpf_crypto.h b/include/linux/bpf_crypto.h
index cf2c66f9782b..e0f946926f69 100644
--- a/include/linux/bpf_crypto.h
+++ b/include/linux/bpf_crypto.h
@@ -18,9 +18,13 @@ struct bpf_crypto_type {
int (*encrypt)(void *tfm, const u8 *src, u8 *dst, unsigned int len, u8 *iv);
int (*decrypt)(void *tfm, const u8 *src, u8 *dst, unsigned int len, u8 *iv);
int (*hash)(void *tfm, const u8 *data, u8 *out, unsigned int len);
+ int (*verify)(void *tfm, const u8 *sig, unsigned int sig_len,
+ const u8 *msg, unsigned int msg_len);
unsigned int (*ivsize)(void *tfm);
unsigned int (*statesize)(void *tfm);
unsigned int (*digestsize)(void *tfm);
+ unsigned int (*keysize)(void *tfm);
+ unsigned int (*maxsize)(void *tfm);
u32 (*get_flags)(void *tfm);
struct module *owner;
enum bpf_crypto_type_id type_id;
diff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c
index 3b2b0d06f1b2..c51288f2e117 100644
--- a/kernel/bpf/crypto.c
+++ b/kernel/bpf/crypto.c
@@ -419,6 +419,117 @@ __bpf_kfunc int bpf_crypto_hash(struct bpf_crypto_ctx *ctx,
}
#endif /* CONFIG_CRYPTO_HASH2 */
+#if IS_ENABLED(CONFIG_CRYPTO_SIG2)
+/**
+ * bpf_sig_verify() - Verify digital signature using configured context
+ * @ctx: The crypto context being used. The ctx must be a trusted pointer.
+ * @message: bpf_dynptr to the message hash to verify. Must be a trusted pointer.
+ * @signature: bpf_dynptr to the signature. Must be a trusted pointer.
+ *
+ * Verifies a digital signature over a message hash using the public key
+ * configured in the crypto context. Supports any signature algorithm
+ * registered with the crypto subsystem (e.g., ECDSA, RSA).
+ *
+ * Return: 0 on success (valid signature), negative error code on failure.
+ */
+__bpf_kfunc int bpf_sig_verify(struct bpf_crypto_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;
+ u64 msg_len, sig_len;
+ const u8 *msg_ptr, *sig_ptr;
+
+ if (ctx->type->type_id != BPF_CRYPTO_TYPE_SIG)
+ return -EINVAL;
+
+ if (!ctx->type->verify)
+ return -EOPNOTSUPP;
+
+ msg_len = __bpf_dynptr_size(msg_kern);
+ sig_len = __bpf_dynptr_size(sig_kern);
+
+ if (msg_len == 0 || msg_len > UINT_MAX)
+ return -EINVAL;
+ if (sig_len == 0 || sig_len > UINT_MAX)
+ 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 ctx->type->verify(ctx->tfm, sig_ptr, sig_len, msg_ptr, msg_len);
+}
+
+/**
+ * bpf_sig_keysize() - Get the key size for signature context
+ * @ctx: The crypto context being used. The ctx must be a trusted pointer.
+ *
+ * Return: The key size in bits, or negative error code on failure.
+ */
+__bpf_kfunc int bpf_sig_keysize(struct bpf_crypto_ctx *ctx)
+{
+ if (ctx->type->type_id != BPF_CRYPTO_TYPE_SIG)
+ return -EINVAL;
+
+ if (!ctx->type->keysize)
+ return -EOPNOTSUPP;
+
+ return ctx->type->keysize(ctx->tfm);
+}
+
+/**
+ * bpf_sig_digestsize() - Get the digest size for signature context
+ * @ctx: The crypto context being used. The ctx must be a trusted pointer.
+ *
+ * Return: The digest size in bytes, or negative error code on failure.
+ */
+__bpf_kfunc int bpf_sig_digestsize(struct bpf_crypto_ctx *ctx)
+{
+ unsigned int size;
+
+ if (ctx->type->type_id != BPF_CRYPTO_TYPE_SIG)
+ return -EINVAL;
+
+ if (!ctx->type->digestsize)
+ return -EOPNOTSUPP;
+
+ size = ctx->type->digestsize(ctx->tfm);
+ if (!size)
+ return -EOPNOTSUPP;
+
+ return size;
+}
+
+/**
+ * bpf_sig_maxsize() - Get the maximum signature size for signature context
+ * @ctx: The crypto context being used. The ctx must be a trusted pointer.
+ *
+ * Return: The maximum signature size in bytes, or negative error code on failure.
+ */
+__bpf_kfunc int bpf_sig_maxsize(struct bpf_crypto_ctx *ctx)
+{
+ unsigned int size;
+
+ if (ctx->type->type_id != BPF_CRYPTO_TYPE_SIG)
+ return -EINVAL;
+
+ if (!ctx->type->maxsize)
+ return -EOPNOTSUPP;
+
+ size = ctx->type->maxsize(ctx->tfm);
+ if (!size)
+ return -EOPNOTSUPP;
+
+ return size;
+}
+#endif /* CONFIG_CRYPTO_SIG2 */
+
__bpf_kfunc_end_defs();
BTF_KFUNCS_START(crypt_init_kfunc_btf_ids)
@@ -438,6 +549,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_SIG2)
+BTF_ID_FLAGS(func, bpf_sig_verify, KF_RCU)
+BTF_ID_FLAGS(func, bpf_sig_keysize)
+BTF_ID_FLAGS(func, bpf_sig_digestsize)
+BTF_ID_FLAGS(func, bpf_sig_maxsize)
+#endif
BTF_KFUNCS_END(crypt_kfunc_btf_ids)
static const struct btf_kfunc_id_set crypt_kfunc_set = {
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH bpf-next v8 4/4] selftests/bpf: Add tests for signature verification kfuncs
2026-02-25 20:29 [PATCH bpf-next v8 0/4] Add cryptographic hash and signature verification kfuncs to BPF Daniel Hodges
` (2 preceding siblings ...)
2026-02-25 20:29 ` [PATCH bpf-next v8 3/4] bpf: Add signature verification kfuncs Daniel Hodges
@ 2026-02-25 20:29 ` Daniel Hodges
3 siblings, 0 replies; 7+ messages in thread
From: Daniel Hodges @ 2026-02-25 20:29 UTC (permalink / raw)
To: bpf, linux-crypto, linux-kernel, linux-kselftest
Cc: ast, andrii, daniel, vadim.fedorenko, song, yatsenko, martin.lau,
eddyz87, haoluo, jolsa, john.fastabend, kpsingh, sdf,
yonghong.song, herbert, davem, Daniel Hodges
Add tests for the signature verification kfuncs:
1. test_ecdsa_verify_valid_signature: Verifies that a valid ECDSA
signature over a known message hash is correctly verified using
the P-256 curve with a test vector.
2. test_ecdsa_verify_invalid_signature: Verifies that an invalid
signature (with modified r component) is correctly rejected.
3. test_ecdsa_size_queries: Tests the bpf_sig_keysize(),
bpf_sig_digestsize(), and bpf_sig_maxsize() kfuncs to ensure
they return valid positive values for a P-256 ECDSA context.
4. test_ecdsa_on_hash_ctx: Tests that calling bpf_sig_verify on
a hash context fails with -EINVAL due to type mismatch.
5. test_ecdsa_keysize_on_hash_ctx: Tests that calling bpf_sig_keysize
on a hash context fails with -EINVAL due to type mismatch.
6. test_ecdsa_zero_len_msg: Tests that zero-length message is rejected.
7. test_ecdsa_zero_len_sig: Tests that zero-length signature is rejected.
The test uses the p1363(ecdsa-nist-p256) algorithm with a known
NIST P-256 test vector for reliable and reproducible testing.
Signed-off-by: Daniel Hodges <git@danielhodges.dev>
---
MAINTAINERS | 2 +
tools/testing/selftests/bpf/config | 4 +
.../selftests/bpf/prog_tests/sig_verify.c | 163 ++++++++++
.../selftests/bpf/progs/crypto_common.h | 6 +
.../testing/selftests/bpf/progs/sig_verify.c | 286 ++++++++++++++++++
5 files changed, 461 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/sig_verify.c
create mode 100644 tools/testing/selftests/bpf/progs/sig_verify.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 2d7d4bc3e7b3..8aed567f868a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4807,7 +4807,9 @@ F: crypto/bpf_crypto_skcipher.c
F: include/linux/bpf_crypto.h
F: kernel/bpf/crypto.c
F: tools/testing/selftests/bpf/prog_tests/crypto_hash.c
+F: tools/testing/selftests/bpf/prog_tests/sig_verify.c
F: tools/testing/selftests/bpf/progs/crypto_hash.c
+F: tools/testing/selftests/bpf/progs/sig_verify.c
BPF [DOCUMENTATION] (Related to Standardization)
R: David Vernet <void@manifault.com>
diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index dcfdca7daea3..947c424425c2 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -21,6 +21,10 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=y
CONFIG_CRYPTO_SKCIPHER=y
CONFIG_CRYPTO_ECB=y
CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_SIG=y
+CONFIG_CRYPTO_SIG2=y
+CONFIG_CRYPTO_ECDSA=y
+CONFIG_CRYPTO_ECRDSA=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_BTF=y
CONFIG_DEBUG_INFO_DWARF4=y
diff --git a/tools/testing/selftests/bpf/prog_tests/sig_verify.c b/tools/testing/selftests/bpf/prog_tests/sig_verify.c
new file mode 100644
index 000000000000..f682fc3c8595
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/sig_verify.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+
+#include <test_progs.h>
+#include "sig_verify.skel.h"
+
+static void test_ecdsa_verify_valid_signature(void)
+{
+ struct sig_verify *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = sig_verify__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load"))
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_ecdsa_verify_valid);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_ecdsa_verify_valid");
+ ASSERT_EQ(skel->data->ctx_create_status, 0, "ctx_create_status");
+ ASSERT_EQ(skel->data->verify_result, 0, "verify_valid_signature");
+
+ sig_verify__destroy(skel);
+}
+
+static void test_ecdsa_verify_invalid_signature(void)
+{
+ struct sig_verify *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = sig_verify__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load"))
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_ecdsa_verify_invalid);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_ecdsa_verify_invalid");
+ ASSERT_NEQ(skel->data->verify_invalid_result, 0, "verify_invalid_signature_rejected");
+
+ sig_verify__destroy(skel);
+}
+
+static void test_ecdsa_size_queries(void)
+{
+ struct sig_verify *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = sig_verify__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load"))
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_ecdsa_size_queries);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_ecdsa_size_queries");
+ ASSERT_EQ(skel->data->ctx_create_status, 0, "ctx_create_status");
+ /* P-256 key size is 256 bits = 32 bytes */
+ ASSERT_GT(skel->data->keysize_result, 0, "keysize_positive");
+ /* P-256 digest size is 32 bytes (SHA-256) */
+ ASSERT_GT(skel->data->digestsize_result, 0, "digestsize_positive");
+ /* P-256 max signature size is 64 bytes (r||s format) */
+ ASSERT_GT(skel->data->maxsize_result, 0, "maxsize_positive");
+
+ sig_verify__destroy(skel);
+}
+
+static void test_ecdsa_on_hash_ctx(void)
+{
+ struct sig_verify *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = sig_verify__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load"))
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_ecdsa_on_hash_ctx);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_ecdsa_on_hash_ctx");
+ ASSERT_EQ(skel->data->ecdsa_on_hash_ctx_status, 0, "ecdsa_on_hash_ctx_rejected");
+
+ sig_verify__destroy(skel);
+}
+
+static void test_ecdsa_keysize_on_hash_ctx(void)
+{
+ struct sig_verify *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = sig_verify__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load"))
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_ecdsa_keysize_on_hash_ctx);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_ecdsa_keysize_on_hash_ctx");
+ ASSERT_EQ(skel->data->ecdsa_keysize_on_hash_status, 0, "ecdsa_keysize_on_hash_rejected");
+
+ sig_verify__destroy(skel);
+}
+
+static void test_ecdsa_zero_len_msg(void)
+{
+ struct sig_verify *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = sig_verify__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load"))
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_ecdsa_zero_len_msg);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_ecdsa_zero_len_msg");
+ ASSERT_EQ(skel->data->ecdsa_zero_msg_status, 0, "zero_len_msg_rejected");
+
+ sig_verify__destroy(skel);
+}
+
+static void test_ecdsa_zero_len_sig(void)
+{
+ struct sig_verify *skel;
+ int err, prog_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ skel = sig_verify__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load"))
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs.test_ecdsa_zero_len_sig);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_ecdsa_zero_len_sig");
+ ASSERT_EQ(skel->data->ecdsa_zero_sig_status, 0, "zero_len_sig_rejected");
+
+ sig_verify__destroy(skel);
+}
+
+void test_sig_verify(void)
+{
+ if (test__start_subtest("verify_valid_signature"))
+ test_ecdsa_verify_valid_signature();
+ if (test__start_subtest("verify_invalid_signature"))
+ test_ecdsa_verify_invalid_signature();
+ if (test__start_subtest("size_queries"))
+ test_ecdsa_size_queries();
+ if (test__start_subtest("ecdsa_on_hash_ctx"))
+ test_ecdsa_on_hash_ctx();
+ if (test__start_subtest("ecdsa_keysize_on_hash_ctx"))
+ test_ecdsa_keysize_on_hash_ctx();
+ if (test__start_subtest("zero_len_msg"))
+ test_ecdsa_zero_len_msg();
+ if (test__start_subtest("zero_len_sig"))
+ test_ecdsa_zero_len_sig();
+}
diff --git a/tools/testing/selftests/bpf/progs/crypto_common.h b/tools/testing/selftests/bpf/progs/crypto_common.h
index 2f04f08f890b..3849083f2d23 100644
--- a/tools/testing/selftests/bpf/progs/crypto_common.h
+++ b/tools/testing/selftests/bpf/progs/crypto_common.h
@@ -17,6 +17,12 @@ int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx, const struct bpf_dynptr *src,
const struct bpf_dynptr *dst, const struct bpf_dynptr *iv) __ksym;
int bpf_crypto_hash(struct bpf_crypto_ctx *ctx, const struct bpf_dynptr *data,
const struct bpf_dynptr *out) __ksym;
+int bpf_sig_verify(struct bpf_crypto_ctx *ctx,
+ const struct bpf_dynptr *message,
+ const struct bpf_dynptr *signature) __ksym;
+int bpf_sig_keysize(struct bpf_crypto_ctx *ctx) __ksym;
+int bpf_sig_digestsize(struct bpf_crypto_ctx *ctx) __ksym;
+int bpf_sig_maxsize(struct bpf_crypto_ctx *ctx) __ksym;
struct __crypto_ctx_value {
struct bpf_crypto_ctx __kptr * ctx;
diff --git a/tools/testing/selftests/bpf/progs/sig_verify.c b/tools/testing/selftests/bpf/progs/sig_verify.c
new file mode 100644
index 000000000000..27488404444d
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/sig_verify.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+#include "crypto_common.h"
+
+/* NIST P-256 test vector
+ * This is a known valid ECDSA signature for testing purposes
+ */
+
+/* Public key in uncompressed format: 0x04 || x || y (65 bytes) */
+unsigned char pubkey_p256[65] = {
+ 0x04, /* Uncompressed point indicator */
+ /* X coordinate (32 bytes) */
+ 0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31,
+ 0xc9, 0x61, 0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68,
+ 0xc0, 0x49, 0xb8, 0x92, 0x3b, 0x61, 0xfa, 0x6c,
+ 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6,
+ /* Y coordinate (32 bytes) */
+ 0x79, 0x03, 0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99,
+ 0xa4, 0x1a, 0xe9, 0xe9, 0x56, 0x28, 0xbc, 0x64,
+ 0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51,
+ 0x77, 0xa3, 0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99
+};
+
+/* Message hash (32 bytes) - SHA-256 of "sample" */
+unsigned char message_hash[32] = {
+ 0xaf, 0x2b, 0xdb, 0xe1, 0xaa, 0x9b, 0x6e, 0xc1,
+ 0xe2, 0xad, 0xe1, 0xd6, 0x94, 0xf4, 0x1f, 0xc7,
+ 0x1a, 0x83, 0x1d, 0x02, 0x68, 0xe9, 0x89, 0x15,
+ 0x62, 0x11, 0x3d, 0x8a, 0x62, 0xad, 0xd1, 0xbf
+};
+
+/* Valid signature r || s (64 bytes) */
+unsigned char valid_signature[64] = {
+ /* r component (32 bytes) */
+ 0xef, 0xd4, 0x8b, 0x2a, 0xac, 0xb6, 0xa8, 0xfd,
+ 0x11, 0x40, 0xdd, 0x9c, 0xd4, 0x5e, 0x81, 0xd6,
+ 0x9d, 0x2c, 0x87, 0x7b, 0x56, 0xaa, 0xf9, 0x91,
+ 0xc3, 0x4d, 0x0e, 0xa8, 0x4e, 0xaf, 0x37, 0x16,
+ /* s component (32 bytes) */
+ 0xf7, 0xcb, 0x1c, 0x94, 0x2d, 0x65, 0x7c, 0x41,
+ 0xd4, 0x36, 0xc7, 0xa1, 0xb6, 0xe2, 0x9f, 0x65,
+ 0xf3, 0xe9, 0x00, 0xdb, 0xb9, 0xaf, 0xf4, 0x06,
+ 0x4d, 0xc4, 0xab, 0x2f, 0x84, 0x3a, 0xcd, 0xa8
+};
+
+/* Invalid signature (modified r component) for negative test */
+unsigned char invalid_signature[64] = {
+ /* r component (32 bytes) - first byte modified */
+ 0xff, 0xd4, 0x8b, 0x2a, 0xac, 0xb6, 0xa8, 0xfd,
+ 0x11, 0x40, 0xdd, 0x9c, 0xd4, 0x5e, 0x81, 0xd6,
+ 0x9d, 0x2c, 0x87, 0x7b, 0x56, 0xaa, 0xf9, 0x91,
+ 0xc3, 0x4d, 0x0e, 0xa8, 0x4e, 0xaf, 0x37, 0x16,
+ /* s component (32 bytes) */
+ 0xf7, 0xcb, 0x1c, 0x94, 0x2d, 0x65, 0x7c, 0x41,
+ 0xd4, 0x36, 0xc7, 0xa1, 0xb6, 0xe2, 0x9f, 0x65,
+ 0xf3, 0xe9, 0x00, 0xdb, 0xb9, 0xaf, 0xf4, 0x06,
+ 0x4d, 0xc4, 0xab, 0x2f, 0x84, 0x3a, 0xcd, 0xa8
+};
+
+/* Test results */
+int verify_result = -1;
+int verify_invalid_result = -1;
+int ctx_create_status = -1;
+int keysize_result = -1;
+int digestsize_result = -1;
+int maxsize_result = -1;
+int ecdsa_on_hash_ctx_status = -1;
+int ecdsa_keysize_on_hash_status = -1;
+int ecdsa_zero_msg_status = -1;
+int ecdsa_zero_sig_status = -1;
+
+SEC("syscall")
+int test_ecdsa_verify_valid(void *ctx)
+{
+ struct bpf_crypto_ctx *ecdsa_ctx;
+ struct bpf_crypto_params params = {
+ .type = "sig",
+ .algo = "p1363(ecdsa-nist-p256)",
+ .key_len = sizeof(pubkey_p256),
+ };
+ struct bpf_dynptr msg_ptr, sig_ptr;
+ int err = 0;
+
+ __builtin_memcpy(params.key, pubkey_p256, sizeof(pubkey_p256));
+
+ ecdsa_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!ecdsa_ctx) {
+ ctx_create_status = err;
+ return 0;
+ }
+ ctx_create_status = 0;
+
+ bpf_dynptr_from_mem(message_hash, sizeof(message_hash), 0, &msg_ptr);
+ bpf_dynptr_from_mem(valid_signature, sizeof(valid_signature), 0, &sig_ptr);
+
+ verify_result = bpf_sig_verify(ecdsa_ctx, &msg_ptr, &sig_ptr);
+
+ bpf_crypto_ctx_release(ecdsa_ctx);
+
+ return 0;
+}
+
+SEC("syscall")
+int test_ecdsa_verify_invalid(void *ctx)
+{
+ struct bpf_crypto_ctx *ecdsa_ctx;
+ struct bpf_crypto_params params = {
+ .type = "sig",
+ .algo = "p1363(ecdsa-nist-p256)",
+ .key_len = sizeof(pubkey_p256),
+ };
+ struct bpf_dynptr msg_ptr, sig_ptr;
+ int err = 0;
+
+ __builtin_memcpy(params.key, pubkey_p256, sizeof(pubkey_p256));
+
+ ecdsa_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!ecdsa_ctx)
+ return 0;
+
+ bpf_dynptr_from_mem(message_hash, sizeof(message_hash), 0, &msg_ptr);
+ bpf_dynptr_from_mem(invalid_signature, sizeof(invalid_signature), 0, &sig_ptr);
+
+ verify_invalid_result = bpf_sig_verify(ecdsa_ctx, &msg_ptr, &sig_ptr);
+
+ bpf_crypto_ctx_release(ecdsa_ctx);
+
+ return 0;
+}
+
+SEC("syscall")
+int test_ecdsa_size_queries(void *ctx)
+{
+ struct bpf_crypto_ctx *ecdsa_ctx;
+ struct bpf_crypto_params params = {
+ .type = "sig",
+ .algo = "p1363(ecdsa-nist-p256)",
+ .key_len = sizeof(pubkey_p256),
+ };
+ int err = 0;
+
+ __builtin_memcpy(params.key, pubkey_p256, sizeof(pubkey_p256));
+
+ ecdsa_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!ecdsa_ctx) {
+ ctx_create_status = err;
+ return 0;
+ }
+ ctx_create_status = 0;
+
+ keysize_result = bpf_sig_keysize(ecdsa_ctx);
+ digestsize_result = bpf_sig_digestsize(ecdsa_ctx);
+ maxsize_result = bpf_sig_maxsize(ecdsa_ctx);
+
+ bpf_crypto_ctx_release(ecdsa_ctx);
+
+ return 0;
+}
+
+/* Test that calling bpf_sig_verify on hash context fails with type mismatch */
+SEC("syscall")
+int test_ecdsa_on_hash_ctx(void *ctx)
+{
+ struct bpf_crypto_ctx *hash_ctx;
+ struct bpf_crypto_params params = {
+ .type = "hash",
+ .algo = "sha256",
+ .key_len = 0,
+ };
+ struct bpf_dynptr msg_ptr, sig_ptr;
+ int err = 0;
+ int ret;
+
+ hash_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!hash_ctx) {
+ ecdsa_on_hash_ctx_status = err;
+ return 0;
+ }
+
+ bpf_dynptr_from_mem(message_hash, sizeof(message_hash), 0, &msg_ptr);
+ bpf_dynptr_from_mem(valid_signature, sizeof(valid_signature), 0, &sig_ptr);
+
+ ret = bpf_sig_verify(hash_ctx, &msg_ptr, &sig_ptr);
+ /* Expected: should fail with -EINVAL (-22) due to type_id mismatch */
+ ecdsa_on_hash_ctx_status = (ret == -22) ? 0 : ret;
+ bpf_crypto_ctx_release(hash_ctx);
+ return 0;
+}
+
+/* Test that calling bpf_sig_keysize on hash context fails with type mismatch */
+SEC("syscall")
+int test_ecdsa_keysize_on_hash_ctx(void *ctx)
+{
+ struct bpf_crypto_ctx *hash_ctx;
+ struct bpf_crypto_params params = {
+ .type = "hash",
+ .algo = "sha256",
+ .key_len = 0,
+ };
+ int err = 0;
+ int ret;
+
+ hash_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!hash_ctx) {
+ ecdsa_keysize_on_hash_status = err;
+ return 0;
+ }
+
+ ret = bpf_sig_keysize(hash_ctx);
+ /* Expected: should fail with -EINVAL (-22) due to type_id mismatch */
+ ecdsa_keysize_on_hash_status = (ret == -22) ? 0 : ret;
+ bpf_crypto_ctx_release(hash_ctx);
+ return 0;
+}
+
+/* Test that bpf_sig_verify with zero-length message fails */
+SEC("syscall")
+int test_ecdsa_zero_len_msg(void *ctx)
+{
+ struct bpf_crypto_ctx *ecdsa_ctx;
+ struct bpf_crypto_params params = {
+ .type = "sig",
+ .algo = "p1363(ecdsa-nist-p256)",
+ .key_len = sizeof(pubkey_p256),
+ };
+ struct bpf_dynptr msg_ptr, sig_ptr;
+ int err = 0;
+ int ret;
+
+ __builtin_memcpy(params.key, pubkey_p256, sizeof(pubkey_p256));
+
+ ecdsa_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!ecdsa_ctx) {
+ ecdsa_zero_msg_status = err;
+ return 0;
+ }
+
+ /* Zero-length message */
+ bpf_dynptr_from_mem(message_hash, 0, 0, &msg_ptr);
+ bpf_dynptr_from_mem(valid_signature, sizeof(valid_signature), 0, &sig_ptr);
+
+ ret = bpf_sig_verify(ecdsa_ctx, &msg_ptr, &sig_ptr);
+ /* Expected: should fail with -EINVAL (-22) */
+ ecdsa_zero_msg_status = (ret == -22) ? 0 : ret;
+ bpf_crypto_ctx_release(ecdsa_ctx);
+ return 0;
+}
+
+/* Test that bpf_sig_verify with zero-length signature fails */
+SEC("syscall")
+int test_ecdsa_zero_len_sig(void *ctx)
+{
+ struct bpf_crypto_ctx *ecdsa_ctx;
+ struct bpf_crypto_params params = {
+ .type = "sig",
+ .algo = "p1363(ecdsa-nist-p256)",
+ .key_len = sizeof(pubkey_p256),
+ };
+ struct bpf_dynptr msg_ptr, sig_ptr;
+ int err = 0;
+ int ret;
+
+ __builtin_memcpy(params.key, pubkey_p256, sizeof(pubkey_p256));
+
+ ecdsa_ctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err);
+ if (!ecdsa_ctx) {
+ ecdsa_zero_sig_status = err;
+ return 0;
+ }
+
+ bpf_dynptr_from_mem(message_hash, sizeof(message_hash), 0, &msg_ptr);
+ /* Zero-length signature */
+ bpf_dynptr_from_mem(valid_signature, 0, 0, &sig_ptr);
+
+ ret = bpf_sig_verify(ecdsa_ctx, &msg_ptr, &sig_ptr);
+ /* Expected: should fail with -EINVAL (-22) */
+ ecdsa_zero_sig_status = (ret == -22) ? 0 : ret;
+ bpf_crypto_ctx_release(ecdsa_ctx);
+ return 0;
+}
+
+char __license[] SEC("license") = "GPL";
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next v8 3/4] bpf: Add signature verification kfuncs
2026-02-25 20:29 ` [PATCH bpf-next v8 3/4] bpf: Add signature verification kfuncs Daniel Hodges
@ 2026-02-25 21:06 ` bot+bpf-ci
0 siblings, 0 replies; 7+ messages in thread
From: bot+bpf-ci @ 2026-02-25 21:06 UTC (permalink / raw)
To: git, bpf, linux-crypto, linux-kernel, linux-kselftest
Cc: ast, andrii, daniel, vadim.fedorenko, song, yatsenko, martin.lau,
eddyz87, haoluo, jolsa, john.fastabend, kpsingh, sdf,
yonghong.song, herbert, davem, git, ast, andrii, daniel,
martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2189 bytes --]
> Add enum bpf_crypto_type_id for runtime type checking to ensure
> operations are performed on the correct crypto context type. The enum
> values are assigned to all crypto type modules (skcipher, hash, sig).
This paragraph appears to be inaccurate. The enum bpf_crypto_type_id and
all three values (BPF_CRYPTO_TYPE_SKCIPHER, BPF_CRYPTO_TYPE_HASH,
BPF_CRYPTO_TYPE_SIG) were added in the earlier commit ddee9f01e40e
("bpf: Add hash kfunc for cryptographic hashing"), along with the
type_id field in struct bpf_crypto_type and the assignment to the
skcipher module.
Should this paragraph be removed or reworded to reflect what this
commit actually introduces?
> diff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c
> --- a/kernel/bpf/crypto.c
> +++ b/kernel/bpf/crypto.c
[ ... ]
> @@ -438,6 +549,12 @@ __bpf_kfunc int bpf_sig_maxsize(struct bpf_crypto_ctx *ctx)
> #if IS_ENABLED(CONFIG_CRYPTO_HASH2)
> BTF_ID_FLAGS(func, bpf_crypto_hash, KF_RCU)
> #endif
> +#if IS_ENABLED(CONFIG_CRYPTO_SIG2)
> +BTF_ID_FLAGS(func, bpf_sig_verify, KF_RCU)
> +BTF_ID_FLAGS(func, bpf_sig_keysize)
> +BTF_ID_FLAGS(func, bpf_sig_digestsize)
> +BTF_ID_FLAGS(func, bpf_sig_maxsize)
> +#endif
This isn't a bug, but the existing kfuncs in this file follow the
bpf_crypto_ naming convention: bpf_crypto_encrypt, bpf_crypto_decrypt,
bpf_crypto_hash. The new kfuncs use bpf_sig_ instead. Would
bpf_crypto_sig_verify (and similar) be more consistent for API
discoverability?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/22414975518
AI-authorship-score: medium
AI-authorship-explanation: Formulaic boilerplate code and polished commit message suggest possible AI assistance, but iterative v1-v8 development with lore interaction and a human-like commit message inaccuracy pattern indicate primarily human authorship.
issues-found: 2
issue-severity-score: low
issue-severity-explanation: Commit message inaccuracy about enum addition and kfunc naming inconsistency are documentation and style issues with no runtime impact.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next v8 1/4] bpf: Add hash kfunc for cryptographic hashing
2026-02-25 20:29 ` [PATCH bpf-next v8 1/4] bpf: Add hash kfunc for cryptographic hashing Daniel Hodges
@ 2026-02-25 21:06 ` bot+bpf-ci
0 siblings, 0 replies; 7+ messages in thread
From: bot+bpf-ci @ 2026-02-25 21:06 UTC (permalink / raw)
To: git, bpf, linux-crypto, linux-kernel, linux-kselftest
Cc: ast, andrii, daniel, vadim.fedorenko, song, yatsenko, martin.lau,
eddyz87, haoluo, jolsa, john.fastabend, kpsingh, sdf,
yonghong.song, herbert, davem, git, ast, andrii, daniel,
martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3896 bytes --]
> diff --git a/crypto/bpf_crypto_shash.c b/crypto/bpf_crypto_shash.c
> --- /dev/null
> +++ b/crypto/bpf_crypto_shash.c
> @@ -0,0 +1,96 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
> +#include <linux/types.h>
> +#include <linux/module.h>
> +#include <linux/bpf_crypto.h>
> +#include <crypto/hash.h>
> +
> +struct bpf_shash_ctx {
> + struct crypto_shash *tfm;
> + struct shash_desc desc;
> +};
[ ... ]
> +static void *bpf_crypto_shash_alloc_tfm(const char *algo)
> +{
[ ... ]
> + ctx->tfm = tfm;
> + ctx->desc.tfm = tfm;
> +
> + return ctx;
> +}
[ ... ]
> +static int bpf_crypto_shash_hash(void *tfm, const u8 *data, u8 *out,
> + unsigned int len)
> +{
> + struct bpf_shash_ctx *ctx = tfm;
> +
> + return crypto_shash_digest(&ctx->desc, data, len, out);
> +}
bpf_crypto_shash_alloc_tfm() creates one bpf_shash_ctx per
bpf_crypto_ctx, with a single embedded shash_desc. Multiple BPF
programs can acquire references to the same bpf_crypto_ctx
concurrently via bpf_crypto_ctx_acquire() (which just does
refcount_inc_not_zero), and the hash kfunc is marked KF_RCU which
provides no mutual exclusion.
When two CPUs call bpf_crypto_hash() with the same context, both
end up in crypto_shash_digest() using the same desc:
CPU0 CPU1
bpf_crypto_hash(ctx) bpf_crypto_hash(ctx)
bpf_crypto_shash_hash() bpf_crypto_shash_hash()
crypto_shash_digest(desc) crypto_shash_digest(desc)
alg->digest(desc, ...) alg->digest(desc, ...)
// writes desc->__ctx // writes desc->__ctx
memset(desc->__ctx, 0) memset(desc->__ctx, 0)
crypto_shash_op_and_zero() in crypto/shash.c writes to
shash_desc_ctx(desc) via the algorithm callback and then zeroes it
with memset. Both CPUs race on this buffer, producing silently
corrupted hash output.
The existing encrypt/decrypt path does not have this problem because
crypto_lskcipher_encrypt() treats the tfm as read-only (the key
schedule is pre-computed) and the mutable IV state is passed
per-call through the siv dynptr.
Would it make sense to allocate a shash_desc per-call on the stack,
or use a per-cpu descriptor, to avoid this race?
> diff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c
> --- a/kernel/bpf/crypto.c
> +++ b/kernel/bpf/crypto.c
[ ... ]
> +#if IS_ENABLED(CONFIG_CRYPTO_HASH2)
> +/**
> + * bpf_crypto_hash() - Compute hash using configured context
> + * @ctx: The crypto context being used. The ctx must be a trusted pointer.
> + * @data: bpf_dynptr to the input data to hash. Must be a trusted pointer.
> + * @out: bpf_dynptr to the output buffer. Must be a trusted pointer.
> + *
> + * Computes hash of the input data using the crypto context. The output buffer
> + * must be at least as large as the digest size of the hash algorithm.
> + */
> +__bpf_kfunc int bpf_crypto_hash(struct bpf_crypto_ctx *ctx,
> + const struct bpf_dynptr *data,
> + const struct bpf_dynptr *out)
> +{
[ ... ]
> + return ctx->type->hash(ctx->tfm, data_ptr, out_ptr, data_len);
> +}
> +#endif /* CONFIG_CRYPTO_HASH2 */
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/22414975518
AI-authorship-score: low
AI-authorship-explanation: Eight revision iterations with active reviewer engagement on lore.kernel.org and incremental changes addressing feedback are consistent with human-driven development.
issues-found: 1
issue-severity-score: medium
issue-severity-explanation: Data race on shared shash_desc in bpf_crypto_hash() produces silently corrupted cryptographic hash output when two CPUs use the same context concurrently, which could affect integrity checking and authentication use cases.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-02-25 21:06 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-25 20:29 [PATCH bpf-next v8 0/4] Add cryptographic hash and signature verification kfuncs to BPF Daniel Hodges
2026-02-25 20:29 ` [PATCH bpf-next v8 1/4] bpf: Add hash kfunc for cryptographic hashing Daniel Hodges
2026-02-25 21:06 ` bot+bpf-ci
2026-02-25 20:29 ` [PATCH bpf-next v8 2/4] selftests/bpf: Add tests for bpf_crypto_hash kfunc Daniel Hodges
2026-02-25 20:29 ` [PATCH bpf-next v8 3/4] bpf: Add signature verification kfuncs Daniel Hodges
2026-02-25 21:06 ` bot+bpf-ci
2026-02-25 20:29 ` [PATCH bpf-next v8 4/4] selftests/bpf: Add tests for " Daniel Hodges
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox