Re: [PATCH bpf-next v2 2/5] bpf: Add SHA hash kfunc for cryptographic hashing

Mykyta Yatsenko
2025-12-05T18:50:53Z

On 12/5/25 17:39, Daniel Hodges wrote:
> Extend bpf_crypto_type structure with hash operations:
> - hash(): Performs hashing operation
> - digestsize(): Returns hash output size
>
> 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
>
> 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.
>
> Signed-off-by: Daniel Hodges <git@danielhodges.dev>
> ---
> include/linux/bpf_crypto.h | 2 +
> kernel/bpf/crypto.c | 76 ++++++++++++++++++++++++++++++++++----
> 2 files changed, 70 insertions(+), 8 deletions(-)
>
> diff --git a/include/linux/bpf_crypto.h b/include/linux/bpf_crypto.h
> index a41e71d4e2d9..c84371cc4e47 100644
> --- a/include/linux/bpf_crypto.h
> +++ b/include/linux/bpf_crypto.h
> @@ -11,8 +11,10 @@ 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;
> char name[14];
> diff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c
> index 83c4d9943084..95625c7ffb1a 100644
> --- a/kernel/bpf/crypto.c
> +++ b/kernel/bpf/crypto.c
> @@ -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,19 @@ 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) {
> + *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);
>
> @@ -343,6 +351,54 @@ __bpf_kfunc int bpf_crypto_encrypt(struct bpf_crypto_ctx *ctx,
> 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;
> + u32 data_len, out_len;
> + const u8 *data_ptr;
> + u8 *out_ptr;
> +
> + if (!ctx->type->hash)
> + return -EOPNOTSUPP;
> +
> + data_len = __bpf_dynptr_size(data_kern);
> + out_len = __bpf_dynptr_size(out_kern);
__bpf_dynptr_size() returns u64, as well as __bpf_dynptr_data_rw()
takes u64 as length parameter, it may be worth using that type for
data_len and out_len.
> +
> + if (data_len == 0)
> + return -EINVAL;
> +
> + if (!ctx->type->digestsize)
> + return -EOPNOTSUPP;
> +
> + unsigned int 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)
> @@ -359,6 +415,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 = {
> @@ -383,6 +442,7 @@ 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);
> + 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,