On 05/01/2026 17:37, Daniel Hodges wrote:
Add support for ECDSA signature verification in BPF programs through the unified bpf_crypto_ctx API.
Changes:
- Add enum bpf_crypto_type_id for efficient type checking
- Update all crypto type modules to set type_id field
- Implement bpf_ecdsa_verify() for signature verification
- Add bpf_ecdsa_keysize(), bpf_ecdsa_digestsize(), bpf_ecdsa_maxsize() helper functions for querying context properties
- Add type_id checks in all ECDSA kfuncs for type safety
- Register ECDSA kfuncs for SCHED_CLS and XDP program types
ECDSA contexts are created using bpf_crypto_ctx_create() with type="sig" and appropriate algorithm (e.g., "p1363(ecdsa-nist-p256)"). The public key is passed via the key/key_len fields in bpf_crypto_params.
This enables BPF programs to perform cryptographic signature verification for use cases such as packet authentication and content validation.
Signed-off-by: Daniel Hodges git@danielhodges.dev
crypto/bpf_crypto_shash.c | 1 + crypto/bpf_crypto_sig.c | 1 + crypto/bpf_crypto_skcipher.c | 1 + include/linux/bpf_crypto.h | 7 +++ kernel/bpf/crypto.c | 115 +++++++++++++++++++++++++++++++++++ 5 files changed, 125 insertions(+)
diff --git a/crypto/bpf_crypto_shash.c b/crypto/bpf_crypto_shash.c index 95c178ec0ce8..6e9b0d757ec9 100644 --- a/crypto/bpf_crypto_shash.c +++ b/crypto/bpf_crypto_shash.c @@ -74,6 +74,7 @@ static const struct bpf_crypto_type bpf_crypto_shash_type = { .digestsize = bpf_crypto_shash_digestsize, .get_flags = bpf_crypto_shash_get_flags, .owner = THIS_MODULE,
- .type_id = BPF_CRYPTO_TYPE_HASH, .name = "hash", };
diff --git a/crypto/bpf_crypto_sig.c b/crypto/bpf_crypto_sig.c index ad0d3810df8e..c6e67338cd40 100644 --- a/crypto/bpf_crypto_sig.c +++ b/crypto/bpf_crypto_sig.c @@ -38,6 +38,7 @@ static const struct bpf_crypto_type bpf_crypto_sig_type = { .get_flags = bpf_crypto_sig_get_flags, .setkey = bpf_crypto_sig_setkey, .owner = THIS_MODULE,
- .type_id = BPF_CRYPTO_TYPE_SIG, .name = "sig", };
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 c84371cc4e47..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);
@@ -17,6 +23,7 @@ struct bpf_crypto_type { 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 f593e7910d3d..3c57a8c31ea2 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,7 @@ struct bpf_crypto_ctx { refcount_t usage; };
- int bpf_crypto_register_type(const struct bpf_crypto_type *type) { struct bpf_crypto_type_list *node;
@@ -400,6 +402,109 @@ __bpf_kfunc int bpf_crypto_hash(struct bpf_crypto_ctx *ctx, } #endif /* CONFIG_CRYPTO_HASH2 */ +#if IS_ENABLED(CONFIG_CRYPTO_ECDSA) +/**
- bpf_ecdsa_verify() - Verify ECDSA signature using pre-allocated context
- @ctx: ECDSA context created by bpf_crypto_ctx_create() with type "sig"
- @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_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;
- struct crypto_sig *sig_tfm;
- const u8 *msg_ptr, *sig_ptr;
- u32 msg_len, sig_len;
- if (!ctx)
return -EINVAL;- if (ctx->type->type_id != BPF_CRYPTO_TYPE_SIG)
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;- sig_tfm = (struct crypto_sig *)ctx->tfm;
- return crypto_sig_verify(sig_tfm, sig_ptr, sig_len, msg_ptr, msg_len);
+}
+/**
- 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_crypto_ctx *ctx) +{
- struct crypto_sig *sig_tfm;
- if (!ctx)
return -EINVAL;- if (ctx->type->type_id != BPF_CRYPTO_TYPE_SIG)
return -EINVAL;- sig_tfm = (struct crypto_sig *)ctx->tfm;
- return crypto_sig_keysize(sig_tfm);
+}
+/**
- bpf_ecdsa_digestsize() - Get the maximum digest size for ECDSA context
- @ctx: ECDSA context
- */
+__bpf_kfunc int bpf_ecdsa_digestsize(struct bpf_crypto_ctx *ctx) +{
- struct crypto_sig *sig_tfm;
- if (!ctx)
return -EINVAL;- if (ctx->type->type_id != BPF_CRYPTO_TYPE_SIG)
return -EINVAL;- sig_tfm = (struct crypto_sig *)ctx->tfm;
- return crypto_sig_digestsize(sig_tfm);
+}
+/**
- bpf_ecdsa_maxsize() - Get the maximum signature size for ECDSA context
- @ctx: ECDSA context
- */
+__bpf_kfunc int bpf_ecdsa_maxsize(struct bpf_crypto_ctx *ctx) +{
- struct crypto_sig *sig_tfm;
- if (!ctx)
return -EINVAL;- if (ctx->type->type_id != BPF_CRYPTO_TYPE_SIG)
return -EINVAL;- sig_tfm = (struct crypto_sig *)ctx->tfm;
- return crypto_sig_maxsize(sig_tfm);
+} +#endif /* CONFIG_CRYPTO_ECDSA */
- __bpf_kfunc_end_defs();
BTF_KFUNCS_START(crypt_init_kfunc_btf_ids) @@ -419,6 +524,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, KF_RCU) +BTF_ID_FLAGS(func, bpf_ecdsa_keysize, 0) +BTF_ID_FLAGS(func, bpf_ecdsa_digestsize, 0) +BTF_ID_FLAGS(func, bpf_ecdsa_maxsize, 0)
Why keysize/digestsize/maxsize are introduced as kfuncs? My understanding is that only bpf_sig_verify() has to be introduced. And no ECDSA in the name as it's one of the types of signature algos.
+#endif BTF_KFUNCS_END(crypt_kfunc_btf_ids) static const struct btf_kfunc_id_set crypt_kfunc_set = { @@ -447,6 +558,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);
I'm pretty sure it will fail because init functions have sleepable *alloc() calls in the implementation and cannot be called in XDP/SCHED context.
return ret ?: register_btf_id_dtor_kfuncs(bpf_crypto_dtors, ARRAY_SIZE(bpf_crypto_dtors), THIS_MODULE);