From nobody Sat Feb 7 11:31:28 2026 Received: from devnull.danielhodges.dev (vps-2f6e086e.vps.ovh.us [135.148.138.8]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 69458201113; Sat, 24 Jan 2026 17:43:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=135.148.138.8 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769276640; cv=none; b=PMx9q+Cr4Cbe89ICGaQsoe+WKZ8+Oy5av3Jm3NwA/jUOsF133tPebQ9dtk9JkscOR0BrYyNIBdlrhFfVHGNcnoaear01OqKF/y7tYPaKC8HMkFLCEGg6/bcqSJmvl7F/sJoBoGS2XUADyrE+FSqiFky2KcGk3SeX0kqAPIq+ruw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769276640; c=relaxed/simple; bh=lGaMEly+Q7J+iLnrE+XDsFx4MoD6SKDPJxsgblFMtas=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sYho7GF0XwIY5oR6ecpzS1+xxHwuPiUx0AnE0sgggW0QGM4yUz2VeSpR4UTtbsFW6EOF+TaXAibzpLxATnmiC4ezGMZwBu+vV4MWRwOQoQ28C41VtbsAwBcw01RcBdZx3TXDXXlUtuFmWEy+H9oakqUUL/KLVeHk+uvd7OG5GZM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=danielhodges.dev; spf=pass smtp.mailfrom=danielhodges.dev; dkim=pass (2048-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b=OYrpI5rz; dkim=permerror (0-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b=ZCFuYvpy; arc=none smtp.client-ip=135.148.138.8 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=danielhodges.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=danielhodges.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b="OYrpI5rz"; dkim=permerror (0-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b="ZCFuYvpy" DKIM-Signature: v=1; a=rsa-sha256; s=202510r; d=danielhodges.dev; c=relaxed/relaxed; h=Message-ID:Date:Subject:To:From; t=1769276630; bh=pDcOpcFqfn9r0tfsFbxs64J rx4xZDI37Kcl72tNx8ZA=; b=OYrpI5rzKoG95smEb4sUfHcGrNPw/lCYWbUvF4SNXowHxKtGmU dyYQxtwI7NQvHCJj0FkNLI4kTLwdtfMx+r4tV+fstWx43ZQ2a0D4fcu0PggWGpB0XshPBJiwfz0 qIfDpwBKqk5zu5WBlEZf1mk4DyLLqbx2qUi+Kj3yyaI/NP5C95EoeiwvxQNTAWYzTx1zawPdRGz Xclzi2RtFhXOF/rby8QAqvBwGDGRjwY104ekudKcRY2eHvRfnj11o/K/v0dHHwhd/TWdRDoas2M PGO2ANNGhty2lmr1ETx0mFaKYhvL4oHjajs5mjTiCiJAl6Vt1+e1u7OSl+LRsNhQIbA==; DKIM-Signature: v=1; a=ed25519-sha256; s=202510e; d=danielhodges.dev; c=relaxed/relaxed; h=Message-ID:Date:Subject:To:From; t=1769276630; bh=pDcOpcFqfn9r0tfsFbxs64J rx4xZDI37Kcl72tNx8ZA=; b=ZCFuYvpyC1V3Gl773g0QEUKtryGSYkC//NHqN6RlA24dYZ5XEy zDLYEfWVvvSlwQS8z+FpV68OqHHZZLqPVuDQ==; From: Daniel Hodges To: bpf@vger.kernel.org Cc: ast@kernel.org, andrii@kernel.org, daniel@iogearbox.net, vadim.fedorenko@linux.dev, song@kernel.org, yatsenko@meta.com, martin.lau@linux.dev, eddyz87@gmail.com, haoluo@google.com, jolsa@kernel.org, john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me, yonghong.song@linux.dev, herbert@gondor.apana.org.au, davem@davemloft.net, linux-crypto@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Daniel Hodges Subject: [PATCH bpf-next v6 1/4] bpf: Add hash kfunc for cryptographic hashing Date: Sat, 24 Jan 2026 12:43:46 -0500 Message-ID: <20260124174349.16861-2-git@danielhodges.dev> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260124174349.16861-1-git@danielhodges.dev> References: <20260124174349.16861-1-git@danielhodges.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- MAINTAINERS | 1 + crypto/Makefile | 3 ++ crypto/bpf_crypto_shash.c | 96 ++++++++++++++++++++++++++++++++++++++ include/linux/bpf_crypto.h | 9 ++++ kernel/bpf/crypto.c | 93 ++++++++++++++++++++++++++++++++---- 5 files changed, 193 insertions(+), 9 deletions(-) create mode 100644 crypto/bpf_crypto_shash.c diff --git a/MAINTAINERS b/MAINTAINERS index 491d567f7dc8..4e9b369acd1c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4713,6 +4713,7 @@ BPF [CRYPTO] M: Vadim Fedorenko 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 16a35649dd91..853dff375906 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -30,6 +30,9 @@ obj-$(CONFIG_CRYPTO_ECHAINIV) +=3D echainiv.o crypto_hash-y +=3D ahash.o crypto_hash-y +=3D shash.o obj-$(CONFIG_CRYPTO_HASH2) +=3D crypto_hash.o +ifeq ($(CONFIG_BPF_SYSCALL),y) +obj-$(CONFIG_CRYPTO_HASH2) +=3D bpf_crypto_shash.o +endif =20 obj-$(CONFIG_CRYPTO_AKCIPHER2) +=3D akcipher.o obj-$(CONFIG_CRYPTO_SIG2) +=3D 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 +#include +#include +#include + +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 =3D crypto_alloc_shash(algo, 0, 0); + if (IS_ERR(tfm)) + return tfm; + + ctx =3D kzalloc(sizeof(*ctx) + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!ctx) { + crypto_free_shash(tfm); + return ERR_PTR(-ENOMEM); + } + + ctx->tfm =3D tfm; + ctx->desc.tfm =3D tfm; + + return ctx; +} + +static void bpf_crypto_shash_free_tfm(void *tfm) +{ + struct bpf_shash_ctx *ctx =3D 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 =3D tfm; + + return crypto_shash_digest(&ctx->desc, data, len, out); +} + +static unsigned int bpf_crypto_shash_digestsize(void *tfm) +{ + struct bpf_shash_ctx *ctx =3D tfm; + + return crypto_shash_digestsize(ctx->tfm); +} + +static u32 bpf_crypto_shash_get_flags(void *tfm) +{ + struct bpf_shash_ctx *ctx =3D tfm; + + return crypto_shash_get_flags(ctx->tfm); +} + +static const struct bpf_crypto_type bpf_crypto_shash_type =3D { + .alloc_tfm =3D bpf_crypto_shash_alloc_tfm, + .free_tfm =3D bpf_crypto_shash_free_tfm, + .has_algo =3D bpf_crypto_shash_has_algo, + .hash =3D bpf_crypto_shash_hash, + .digestsize =3D bpf_crypto_shash_digestsize, + .get_flags =3D bpf_crypto_shash_get_flags, + .owner =3D THIS_MODULE, + .type_id =3D BPF_CRYPTO_TYPE_HASH, + .name =3D "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 =3D 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/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 =20 +enum bpf_crypto_type_id { + BPF_CRYPTO_TYPE_SKCIPHER =3D 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 *i= v); int (*decrypt)(void *tfm, const u8 *src, u8 *dst, unsigned int len, u8 *i= v); + 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]; }; =20 diff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c index 7e75a1936256..bf14856ab5b1 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; } =20 - 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 =3D -EINVAL; + goto err_module_put; + } + if (!params->key_len && type->setkey) { *err =3D -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; } =20 - *err =3D type->setkey(ctx->tfm, params->key, params->key_len); - if (*err) - goto err_free_tfm; + if (params->key_len) { + if (!type->setkey) { + *err =3D -EINVAL; + goto err_free_tfm; + } + *err =3D type->setkey(ctx->tfm, params->key, params->key_len); + if (*err) + goto err_free_tfm; =20 - if (type->get_flags(ctx->tfm) & CRYPTO_TFM_NEED_KEY) { - *err =3D -EINVAL; - goto err_free_tfm; + if (type->get_flags(ctx->tfm) & CRYPTO_TFM_NEED_KEY) { + *err =3D -EINVAL; + goto err_free_tfm; + } } =20 - ctx->siv_len =3D type->ivsize(ctx->tfm) + type->statesize(ctx->tfm); + if (type->ivsize && type->statesize) + ctx->siv_len =3D type->ivsize(ctx->tfm) + type->statesize(ctx->tfm); =20 refcount_set(&ctx->usage, 1); =20 @@ -325,6 +337,9 @@ __bpf_kfunc int bpf_crypto_decrypt(struct bpf_crypto_ct= x *ctx, const struct bpf_dynptr_kern *dst_kern =3D (struct bpf_dynptr_kern *)dst; const struct bpf_dynptr_kern *siv_kern =3D (struct bpf_dynptr_kern *)siv_= _nullable; =20 + if (ctx->type->type_id !=3D BPF_CRYPTO_TYPE_SKCIPHER) + return -EINVAL; + return bpf_crypto_crypt(ctx, src_kern, dst_kern, siv_kern, true); } =20 @@ -346,9 +361,64 @@ __bpf_kfunc int bpf_crypto_encrypt(struct bpf_crypto_c= tx *ctx, const struct bpf_dynptr_kern *dst_kern =3D (struct bpf_dynptr_kern *)dst; const struct bpf_dynptr_kern *siv_kern =3D (struct bpf_dynptr_kern *)siv_= _nullable; =20 + if (ctx->type->type_id !=3D BPF_CRYPTO_TYPE_SKCIPHER) + return -EINVAL; + return bpf_crypto_crypt(ctx, src_kern, dst_kern, siv_kern, false); } =20 +#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 bu= ffer + * 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 =3D (struct bpf_dynptr_kern *)dat= a; + const struct bpf_dynptr_kern *out_kern =3D (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 !=3D BPF_CRYPTO_TYPE_HASH) + return -EINVAL; + + if (!ctx->type->hash) + return -EOPNOTSUPP; + + data_len =3D __bpf_dynptr_size(data_kern); + out_len =3D __bpf_dynptr_size(out_kern); + + if (data_len =3D=3D 0 || data_len > UINT_MAX) + return -EINVAL; + + if (!ctx->type->digestsize) + return -EOPNOTSUPP; + + digestsize =3D ctx->type->digestsize(ctx->tfm); + if (out_len < digestsize) + return -EINVAL; + + data_ptr =3D __bpf_dynptr_data(data_kern, data_len); + if (!data_ptr) + return -EINVAL; + + out_ptr =3D __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(); =20 BTF_KFUNCS_START(crypt_init_kfunc_btf_ids) @@ -365,6 +435,9 @@ static const struct btf_kfunc_id_set crypt_init_kfunc_s= et =3D { 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) =20 static const struct btf_kfunc_id_set crypt_kfunc_set =3D { @@ -389,6 +462,8 @@ static int __init crypto_kfunc_init(void) ret =3D register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &crypt_kfunc_s= et); ret =3D ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_ACT, &crypt_= kfunc_set); ret =3D ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &crypt_kfunc_= set); + /* Register for SYSCALL programs to enable testing and debugging */ + ret =3D ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &crypt_kf= unc_set); ret =3D ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &crypt_init_kfunc_set); return ret ?: register_btf_id_dtor_kfuncs(bpf_crypto_dtors, --=20 2.52.0 From nobody Sat Feb 7 11:31:28 2026 Received: from devnull.danielhodges.dev (vps-2f6e086e.vps.ovh.us [135.148.138.8]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 00F5D23ABBF; Sat, 24 Jan 2026 17:44:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=135.148.138.8 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769276661; cv=none; b=C6835H23Y88DUBB+NQFmUJnoOQSgBdjvAgolukHEu2rILS8w/c632nMRll4qNti7zoc80i4MGOYsOwBL3Z31bGOVx82MqHpTaDxBhZeIgTS+9LTKD8rD6WiqB4CHeJ3tJXqqbp81Js2FjymI8D5pM+rxq/d2NCAanHLNvm7PNuQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769276661; c=relaxed/simple; bh=P9IUCTKEE+qNa5rg7jIPSA2K1Rc09vA1I4C1mVK4XKk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ETYgVUnCj07SucQwQGaAI0qPmOXa00PbovktQIZHGNLt/6fc7tQhrTolpoN2fJ7RlCG+u+n7XDXMJ1knWgcOZcbK0Y5S5Lkokgt3Cv9Y/IegRkyWr4XMwqJnbX9N0QSCJTc/4LPXHRFzBvUtDq6qHubV8AAOi11IRxXTkC+HNUQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=danielhodges.dev; spf=pass smtp.mailfrom=danielhodges.dev; dkim=pass (2048-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b=DlSKyPuX; dkim=permerror (0-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b=o7uZmJlF; arc=none smtp.client-ip=135.148.138.8 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=danielhodges.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=danielhodges.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b="DlSKyPuX"; dkim=permerror (0-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b="o7uZmJlF" DKIM-Signature: v=1; a=rsa-sha256; s=202510r; d=danielhodges.dev; c=relaxed/relaxed; h=Message-ID:Date:Subject:To:From; t=1769276631; bh=HiUwtWr/NEua2FMfQ76RjXF bXYGP1MZ5nYen0JOWZfg=; b=DlSKyPuXrJPVB96tJF7hX4BLbDAUalfHR94Gsfmg3diWoRzK75 JhOpU+ux/tkPHPeTgZDGIRU9sY2AqwyeYTYId76oKKpWlNmZtWczy//l/bX5GiH+AseAh6g/0kD 95JGu8QSwOfnDz1yusswhhtJykAGXet7UxMX3kg3X9YnhLCz/PR1FwGOZILXW76tQrvoT5imFdg osK0hiwd7S3kPdr4KZwdrHqOaikkxhcAcvmjczIeCTMpc9043+e45FiRG10sCKoz0Gl6Vsqlz0q V2y1PdlsdtvoS0In4E1bNPlAqgej2W4IA2mPAvMFbkPt4Wq2kv9bKSca79n2aNt8+Bw==; DKIM-Signature: v=1; a=ed25519-sha256; s=202510e; d=danielhodges.dev; c=relaxed/relaxed; h=Message-ID:Date:Subject:To:From; t=1769276631; bh=HiUwtWr/NEua2FMfQ76RjXF bXYGP1MZ5nYen0JOWZfg=; b=o7uZmJlFz14CQbY8CVfVfCSIHBP2DGvXvFXkyt0GcuEp28GOWo QucIponqYhvVCpi6RJn+2JWl3t8UHb1+hhCw==; From: Daniel Hodges To: bpf@vger.kernel.org Cc: ast@kernel.org, andrii@kernel.org, daniel@iogearbox.net, vadim.fedorenko@linux.dev, song@kernel.org, yatsenko@meta.com, martin.lau@linux.dev, eddyz87@gmail.com, haoluo@google.com, jolsa@kernel.org, john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me, yonghong.song@linux.dev, herbert@gondor.apana.org.au, davem@davemloft.net, linux-crypto@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Daniel Hodges Subject: [PATCH bpf-next v6 2/4] selftests/bpf: Add tests for bpf_crypto_hash kfunc Date: Sat, 24 Jan 2026 12:43:47 -0500 Message-ID: <20260124174349.16861-3-git@danielhodges.dev> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260124174349.16861-1-git@danielhodges.dev> References: <20260124174349.16861-1-git@danielhodges.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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 4e9b369acd1c..9602b6216ab9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4717,6 +4717,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 =20 BPF [DOCUMENTATION] (Related to Standardization) R: David Vernet diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/b= pf/config index 558839e3c185..814804f71780 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -13,6 +13,8 @@ CONFIG_BPF_SYSCALL=3Dy CONFIG_CGROUP_BPF=3Dy CONFIG_CRYPTO_HMAC=3Dy CONFIG_CRYPTO_SHA256=3Dy +CONFIG_CRYPTO_SHA512=3Dy +CONFIG_CRYPTO_HASH2=3Dy CONFIG_CRYPTO_USER_API=3Dy CONFIG_CRYPTO_USER_API_HASH=3Dy CONFIG_CRYPTO_USER_API_SKCIPHER=3Dy diff --git a/tools/testing/selftests/bpf/prog_tests/crypto_hash.c b/tools/t= esting/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 +#include +#include "crypto_hash.skel.h" + +/* NIST test vectors for SHA-256("abc") */ +static const unsigned char expected_sha256[32] =3D { + 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] =3D { + 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] =3D { + 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 =3D crypto_hash__open_and_load(); + if (!skel) { + /* Skip if kfuncs not available (CONFIG_CRYPTO_HASH2 not set) */ + if (errno =3D=3D ENOENT || errno =3D=3D 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 =3D setup_skel(); + if (!skel) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_sha256); + err =3D 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 =3D setup_skel(); + if (!skel) + return; + prog_fd =3D bpf_program__fd(skel->progs.test_sha384); + err =3D 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 =3D setup_skel(); + if (!skel) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_sha512); + err =3D 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 =3D setup_skel(); + if (!skel) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_sha256_zero_len); + err =3D 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 =3D setup_skel(); + if (!skel) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_hash_with_key_rejected); + err =3D 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 =3D setup_skel(); + if (!skel) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_hash_output_too_small); + err =3D 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_s= mall"); + + 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 =3D setup_skel(); + if (!skel) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_hash_on_skcipher_ctx); + err =3D 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_rejec= ted"); + + 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/test= ing/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 *d= ata, + const struct bpf_dynptr *out) __ksym; =20 struct __crypto_ctx_value { struct bpf_crypto_ctx __kptr * ctx; diff --git a/tools/testing/selftests/bpf/progs/crypto_hash.c b/tools/testin= g/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 +#include "bpf_misc.h" +#include "bpf_kfuncs.h" +#include "crypto_common.h" + +unsigned char test_input[3] =3D "abc"; + +/* Expected SHA-256 hash of "abc" */ +/* ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad= */ +unsigned char expected_sha256[32] =3D { + 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] =3D {}; +unsigned char sha384_output[48] =3D {}; +unsigned char sha512_output[64] =3D {}; +unsigned char small_output[16] =3D {}; /* Intentionally small for output_t= oo_small test */ + +int sha256_status =3D -1; +int sha384_status =3D -1; +int sha512_status =3D -1; +int hash_with_key_status =3D -1; +int hash_output_too_small_status =3D -1; +int hash_on_skcipher_status =3D -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 =3D { + .type =3D "hash", + .algo =3D "sha256", + .key_len =3D 0, + }; + int err =3D 0; + + hash_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!hash_ctx) { + sha256_status =3D 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 =3D 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 =3D { + .type =3D "hash", + .algo =3D "sha384", + .key_len =3D 0, + }; + int err =3D 0; + + hash_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!hash_ctx) { + sha384_status =3D 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 =3D 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 =3D { + .type =3D "hash", + .algo =3D "sha512", + .key_len =3D 0, + }; + int err =3D 0; + + hash_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!hash_ctx) { + sha512_status =3D 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 =3D 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 =3D { + .type =3D "hash", + .algo =3D "sha256", + .key_len =3D 0, + }; + int err =3D 0; + int ret; + + hash_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!hash_ctx) { + sha256_status =3D 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 =3D bpf_crypto_hash(hash_ctx, &input_ptr, &output_ptr); + sha256_status =3D (ret =3D=3D -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 =3D { + .type =3D "hash", + .algo =3D "sha256", + .key_len =3D 16, /* Hash algorithms don't support keys */ + }; + int err =3D 0; + + /* Set some dummy key data */ + params.key[0] =3D 0x01; + params.key[1] =3D 0x02; + + hash_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!hash_ctx) { + /* Expected: should fail with -EINVAL (-22) */ + hash_with_key_status =3D (err =3D=3D -22) ? 0 : err; + return 0; + } + + /* Should not reach here - context creation should have failed */ + hash_with_key_status =3D -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 =3D { + .type =3D "hash", + .algo =3D "sha256", + .key_len =3D 0, + }; + int err =3D 0; + int ret; + + hash_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!hash_ctx) { + hash_output_too_small_status =3D 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 =3D bpf_crypto_hash(hash_ctx, &input_ptr, &output_ptr); + /* Expected: should fail with -EINVAL (-22) */ + hash_output_too_small_status =3D (ret =3D=3D -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 =3D { + .type =3D "skcipher", + .algo =3D "ecb(aes)", + .key_len =3D 16, + }; + int err =3D 0; + int ret; + + params.key[0] =3D 0x00; params.key[1] =3D 0x01; params.key[2] =3D 0x02; p= arams.key[3] =3D 0x03; + params.key[4] =3D 0x04; params.key[5] =3D 0x05; params.key[6] =3D 0x06; p= arams.key[7] =3D 0x07; + params.key[8] =3D 0x08; params.key[9] =3D 0x09; params.key[10] =3D 0x0a; = params.key[11] =3D 0x0b; + params.key[12] =3D 0x0c; params.key[13] =3D 0x0d; params.key[14] =3D 0x0e= ; params.key[15] =3D 0x0f; + + cipher_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!cipher_ctx) { + hash_on_skcipher_status =3D 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 =3D bpf_crypto_hash(cipher_ctx, &input_ptr, &output_ptr); + /* Expected: should fail with -EINVAL (-22) due to type_id mismatch */ + hash_on_skcipher_status =3D (ret =3D=3D -22) ? 0 : ret; + bpf_crypto_ctx_release(cipher_ctx); + return 0; +} + +char __license[] SEC("license") =3D "GPL"; --=20 2.52.0 From nobody Sat Feb 7 11:31:28 2026 Received: from devnull.danielhodges.dev (vps-2f6e086e.vps.ovh.us [135.148.138.8]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D4E993EBF2F; Sat, 24 Jan 2026 17:46:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=135.148.138.8 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769276793; cv=none; b=EMkuY5hQQei0722YWGXHcXTWAonm03E7lCDCvhsneZEHehSYo2kANx/F6nFvf1JTXNEAmmf3ICM0Vt4OdJ7h8ytdc2uusgS36cMD2LZg2yYiZHvf3CccbgNRry/domD5tk09VFqgkZTR1T12S51Mx8kBdWBz54hdG2vIw1QzHQ0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769276793; c=relaxed/simple; bh=G29famJEQiPG9KYf6zhsEgt/S3ObZbdhm6gPu47Sf5c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ADDgWQPJ7fgjO8XIVArXeAHavMKjzFjpVQXdA+WQ2VpZd6Ley9vb23qg/d197sDrWPkMHT4h3ZKLEgX3F2etsnZSQfO+YfnzvVRK99frNTJVmHOOW+j6cljE/ugKHgZr9ja0llt4GzO4jkKo8SOPWPctucyn2+9FdIi9HmyOIxE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=danielhodges.dev; spf=pass smtp.mailfrom=danielhodges.dev; dkim=pass (2048-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b=iSgfaXJ8; dkim=permerror (0-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b=o5a6OISu; arc=none smtp.client-ip=135.148.138.8 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=danielhodges.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=danielhodges.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b="iSgfaXJ8"; dkim=permerror (0-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b="o5a6OISu" DKIM-Signature: v=1; a=rsa-sha256; s=202510r; d=danielhodges.dev; c=relaxed/relaxed; h=Message-ID:Date:Subject:To:From; t=1769276631; bh=3ubB5Qb6m3h/r25mvfrB2Jd 30NzkZ3S0kN9EQ26emv8=; b=iSgfaXJ8EHdMMIngb+YTh45mzMa8dKwtABe0DS3wAkSDl062nI TR+Q+3YrxRHeRfCiMGEusCOJQ1xiszFh/hr+kjejBQgEG8II37WW9Kx5dfVppcVuGppk9g882e6 Yxu+G84egA2hblHfNLtCMWSbq9fLonObsEiVBZ8GHQwkJpC9d3VxC2wEoBPwjUZKRY7M9Jailqa jMXLvGqxy9100jgDyrWVLh/2wxCEsszo2cmLIa/upqrrPMIs/4kwVfKo9BWwq075W/NVmMWJZ5j MZt0o6kVtUHJBRVQO7JYdLUwk+jzY7+yEArdk7qHUPbXawCXpu7EA16cAttLU0KhJ5Q==; DKIM-Signature: v=1; a=ed25519-sha256; s=202510e; d=danielhodges.dev; c=relaxed/relaxed; h=Message-ID:Date:Subject:To:From; t=1769276631; bh=3ubB5Qb6m3h/r25mvfrB2Jd 30NzkZ3S0kN9EQ26emv8=; b=o5a6OISusRFzeoT3gP4zzfLYJKE71J+K2h/QbRE3b1vWN5Uuye en9fOEARuqk5rjVPiSiHqCTBZ8FvKMlR0MAQ==; From: Daniel Hodges To: bpf@vger.kernel.org Cc: ast@kernel.org, andrii@kernel.org, daniel@iogearbox.net, vadim.fedorenko@linux.dev, song@kernel.org, yatsenko@meta.com, martin.lau@linux.dev, eddyz87@gmail.com, haoluo@google.com, jolsa@kernel.org, john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me, yonghong.song@linux.dev, herbert@gondor.apana.org.au, davem@davemloft.net, linux-crypto@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Daniel Hodges Subject: [PATCH bpf-next v6 3/4] bpf: Add signature verification kfuncs Date: Sat, 24 Jan 2026 12:43:48 -0500 Message-ID: <20260124174349.16861-4-git@danielhodges.dev> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260124174349.16861-1-git@danielhodges.dev> References: <20260124174349.16861-1-git@danielhodges.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- MAINTAINERS | 1 + crypto/Makefile | 3 + crypto/bpf_crypto_sig.c | 89 ++++++++++++++++++++++++++ crypto/bpf_crypto_skcipher.c | 1 + include/linux/bpf_crypto.h | 4 ++ kernel/bpf/crypto.c | 117 +++++++++++++++++++++++++++++++++++ 6 files changed, 215 insertions(+) create mode 100644 crypto/bpf_crypto_sig.c diff --git a/MAINTAINERS b/MAINTAINERS index 9602b6216ab9..d23ea38b606f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4714,6 +4714,7 @@ M: Vadim Fedorenko 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 853dff375906..c9ab98b57bc0 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -36,6 +36,9 @@ endif =20 obj-$(CONFIG_CRYPTO_AKCIPHER2) +=3D akcipher.o obj-$(CONFIG_CRYPTO_SIG2) +=3D sig.o +ifeq ($(CONFIG_BPF_SYSCALL),y) +obj-$(CONFIG_CRYPTO_SIG2) +=3D bpf_crypto_sig.o +endif obj-$(CONFIG_CRYPTO_KPP2) +=3D kpp.o obj-$(CONFIG_CRYPTO_HKDF) +=3D hkdf.o =20 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 +#include +#include +#include +#include + +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 ke= ylen) +{ + return crypto_sig_set_pubkey(tfm, key, keylen); +} + +static int bpf_crypto_sig_verify(void *tfm, const u8 *sig, unsigned int si= g_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 =3D 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 =3D crypto_sig_alg(tfm); + + return alg->max_size ? alg->max_size(tfm) : 0; +} + +static const struct bpf_crypto_type bpf_crypto_sig_type =3D { + .alloc_tfm =3D bpf_crypto_sig_alloc_tfm, + .free_tfm =3D bpf_crypto_sig_free_tfm, + .has_algo =3D bpf_crypto_sig_has_algo, + .get_flags =3D bpf_crypto_sig_get_flags, + .setkey =3D bpf_crypto_sig_setkey, + .verify =3D bpf_crypto_sig_verify, + .keysize =3D bpf_crypto_sig_keysize, + .digestsize =3D bpf_crypto_sig_digestsize, + .maxsize =3D bpf_crypto_sig_maxsize, + .owner =3D THIS_MODULE, + .type_id =3D BPF_CRYPTO_TYPE_SIG, + .name =3D "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 =3D 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/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 =3D { .statesize =3D bpf_crypto_lskcipher_statesize, .get_flags =3D bpf_crypto_lskcipher_get_flags, .owner =3D THIS_MODULE, + .type_id =3D BPF_CRYPTO_TYPE_SKCIPHER, .name =3D "skcipher", }; =20 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 *i= v); int (*decrypt)(void *tfm, const u8 *src, u8 *dst, unsigned int len, u8 *i= v); 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 bf14856ab5b1..b763a6c5cdd3 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 */ =20 +#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 p= ointer. + * @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 =3D (struct bpf_dynptr_kern *)mess= age; + const struct bpf_dynptr_kern *sig_kern =3D (struct bpf_dynptr_kern *)sign= ature; + u64 msg_len, sig_len; + const u8 *msg_ptr, *sig_ptr; + + if (ctx->type->type_id !=3D BPF_CRYPTO_TYPE_SIG) + return -EINVAL; + + if (!ctx->type->verify) + return -EOPNOTSUPP; + + msg_len =3D __bpf_dynptr_size(msg_kern); + sig_len =3D __bpf_dynptr_size(sig_kern); + + if (msg_len =3D=3D 0 || msg_len > UINT_MAX) + return -EINVAL; + if (sig_len =3D=3D 0 || sig_len > UINT_MAX) + return -EINVAL; + + msg_ptr =3D __bpf_dynptr_data(msg_kern, msg_len); + if (!msg_ptr) + return -EINVAL; + + sig_ptr =3D __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 bytes, or negative error code on failure. + */ +__bpf_kfunc int bpf_sig_keysize(struct bpf_crypto_ctx *ctx) +{ + if (ctx->type->type_id !=3D 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 !=3D BPF_CRYPTO_TYPE_SIG) + return -EINVAL; + + if (!ctx->type->digestsize) + return -EOPNOTSUPP; + + size =3D 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 !=3D BPF_CRYPTO_TYPE_SIG) + return -EINVAL; + + if (!ctx->type->maxsize) + return -EOPNOTSUPP; + + size =3D ctx->type->maxsize(ctx->tfm); + if (!size) + return -EOPNOTSUPP; + + return size; +} +#endif /* CONFIG_CRYPTO_SIG2 */ + __bpf_kfunc_end_defs(); =20 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) =20 static const struct btf_kfunc_id_set crypt_kfunc_set =3D { --=20 2.52.0 From nobody Sat Feb 7 11:31:28 2026 Received: from devnull.danielhodges.dev (vps-2f6e086e.vps.ovh.us [135.148.138.8]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 18EF946BF; Sat, 24 Jan 2026 17:46:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=135.148.138.8 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769276806; cv=none; b=JKjpuyhTIZVPrNMFkg3rvh/aEgoN5AOSWKi9Ny+LpKQO+1AEzRtE4gH6L0AMM7RVHwYtjYMB/x+MqcVxiB5H4Ce9+v+z2OBwUbXLZn3SIcl08w0CIjHi1g/3mWYGmi1aKfXHCPLBwpD1AnzX737rbr/aFE//4XVfcmW7piTLCFc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769276806; c=relaxed/simple; bh=1Dt61OpWt0Xkfr5BmnQ1uLHzYoD8C14kwvbsJpOsP9g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kBSEOa3whEdJDBggzDIhfj/57Ft5vtFVLDux//7BCCJfGxmQFysWrwHnrJiN/Pts3DtYxm/MXpBdou2EPNAX/zKu/uZgiJ8IWEKIERe2LJWjomk5nuUUqwNvHEGNV59HERtNg9q25xjQRRY4OZVqF3K3xsA/jwRyQW6OiR6NveA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=danielhodges.dev; spf=pass smtp.mailfrom=danielhodges.dev; dkim=pass (2048-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b=MtAEi4ss; dkim=permerror (0-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b=w0MWxwO9; arc=none smtp.client-ip=135.148.138.8 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=danielhodges.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=danielhodges.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b="MtAEi4ss"; dkim=permerror (0-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b="w0MWxwO9" DKIM-Signature: v=1; a=rsa-sha256; s=202510r; d=danielhodges.dev; c=relaxed/relaxed; h=Message-ID:Date:Subject:To:From; t=1769276631; bh=Rw9EQBCoOxu6vic1k34lqkT 21VDDUwvvcZf/n+xBQX0=; b=MtAEi4ssKH+k2A6NB3r5YlLcDyolx87U1O8xarBQvmDXHZq1cI 88rc4IQHJW4ioqnzAq3aYPPY5q/k8mdFHNLFehDzQmhw95BdA/WNTOluhL5nKqGnB1YxFvUO1OO QV4niadAst7cgPSAGXlVwUBxT+fR9iRtkI5QsKgt+F6jYLLzw81HehEZmINjJGzCzzK52wy/Rk4 jRXlm7427nqXHZm/RucGLMxKAqui+HNuYShwEdmxQxQEB0/2xvpZgrYb5KQFt4PznCfxM3XZNEb WITGzbSntMh78A6u0YTrf2jjm9qv+2UEB5taqcBPXizNvIqgmociIK2h0S3bpUL2cYg==; DKIM-Signature: v=1; a=ed25519-sha256; s=202510e; d=danielhodges.dev; c=relaxed/relaxed; h=Message-ID:Date:Subject:To:From; t=1769276631; bh=Rw9EQBCoOxu6vic1k34lqkT 21VDDUwvvcZf/n+xBQX0=; b=w0MWxwO9Y6vwHn/+gKN7B+2IXVLqHwbaBfErOaMDOru5YoagJI JTAz1Hpt0poLfgkBGskMxcngoM7yGJRA5nBQ==; From: Daniel Hodges To: bpf@vger.kernel.org Cc: ast@kernel.org, andrii@kernel.org, daniel@iogearbox.net, vadim.fedorenko@linux.dev, song@kernel.org, yatsenko@meta.com, martin.lau@linux.dev, eddyz87@gmail.com, haoluo@google.com, jolsa@kernel.org, john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me, yonghong.song@linux.dev, herbert@gondor.apana.org.au, davem@davemloft.net, linux-crypto@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Daniel Hodges Subject: [PATCH bpf-next v6 4/4] selftests/bpf: Add tests for signature verification kfuncs Date: Sat, 24 Jan 2026 12:43:49 -0500 Message-ID: <20260124174349.16861-5-git@danielhodges.dev> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260124174349.16861-1-git@danielhodges.dev> References: <20260124174349.16861-1-git@danielhodges.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- MAINTAINERS | 2 + .../selftests/bpf/prog_tests/sig_verify.c | 163 ++++++++++ .../selftests/bpf/progs/crypto_common.h | 6 + .../testing/selftests/bpf/progs/sig_verify.c | 286 ++++++++++++++++++ 4 files changed, 457 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 d23ea38b606f..e297cc18c5f6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4719,7 +4719,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 =20 BPF [DOCUMENTATION] (Related to Standardization) R: David Vernet diff --git a/tools/testing/selftests/bpf/prog_tests/sig_verify.c b/tools/te= sting/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 +#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 =3D sig_verify__open_and_load(); + if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load")) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_ecdsa_verify_valid); + err =3D 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 =3D sig_verify__open_and_load(); + if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load")) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_ecdsa_verify_invalid); + err =3D 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_signatur= e_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 =3D sig_verify__open_and_load(); + if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load")) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_ecdsa_size_queries); + err =3D 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 =3D 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 =3D sig_verify__open_and_load(); + if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load")) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_ecdsa_on_hash_ctx); + err =3D 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_rej= ected"); + + 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 =3D sig_verify__open_and_load(); + if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load")) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_ecdsa_keysize_on_hash_ctx); + err =3D 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 =3D sig_verify__open_and_load(); + if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load")) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_ecdsa_zero_len_msg); + err =3D 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 =3D sig_verify__open_and_load(); + if (!ASSERT_OK_PTR(skel, "sig_verify__open_and_load")) + return; + + prog_fd =3D bpf_program__fd(skel->progs.test_ecdsa_zero_len_sig); + err =3D 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/test= ing/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 *d= ata, 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; =20 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 +#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] =3D { + 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] =3D { + 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] =3D { + /* 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] =3D { + /* 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 =3D -1; +int verify_invalid_result =3D -1; +int ctx_create_status =3D -1; +int keysize_result =3D -1; +int digestsize_result =3D -1; +int maxsize_result =3D -1; +int ecdsa_on_hash_ctx_status =3D -1; +int ecdsa_keysize_on_hash_status =3D -1; +int ecdsa_zero_msg_status =3D -1; +int ecdsa_zero_sig_status =3D -1; + +SEC("syscall") +int test_ecdsa_verify_valid(void *ctx) +{ + struct bpf_crypto_ctx *ecdsa_ctx; + struct bpf_crypto_params params =3D { + .type =3D "sig", + .algo =3D "p1363(ecdsa-nist-p256)", + .key_len =3D sizeof(pubkey_p256), + }; + struct bpf_dynptr msg_ptr, sig_ptr; + int err =3D 0; + + __builtin_memcpy(params.key, pubkey_p256, sizeof(pubkey_p256)); + + ecdsa_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!ecdsa_ctx) { + ctx_create_status =3D err; + return 0; + } + ctx_create_status =3D 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 =3D 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 =3D { + .type =3D "sig", + .algo =3D "p1363(ecdsa-nist-p256)", + .key_len =3D sizeof(pubkey_p256), + }; + struct bpf_dynptr msg_ptr, sig_ptr; + int err =3D 0; + + __builtin_memcpy(params.key, pubkey_p256, sizeof(pubkey_p256)); + + ecdsa_ctx =3D 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 =3D 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 =3D { + .type =3D "sig", + .algo =3D "p1363(ecdsa-nist-p256)", + .key_len =3D sizeof(pubkey_p256), + }; + int err =3D 0; + + __builtin_memcpy(params.key, pubkey_p256, sizeof(pubkey_p256)); + + ecdsa_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!ecdsa_ctx) { + ctx_create_status =3D err; + return 0; + } + ctx_create_status =3D 0; + + keysize_result =3D bpf_sig_keysize(ecdsa_ctx); + digestsize_result =3D bpf_sig_digestsize(ecdsa_ctx); + maxsize_result =3D 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 mismat= ch */ +SEC("syscall") +int test_ecdsa_on_hash_ctx(void *ctx) +{ + struct bpf_crypto_ctx *hash_ctx; + struct bpf_crypto_params params =3D { + .type =3D "hash", + .algo =3D "sha256", + .key_len =3D 0, + }; + struct bpf_dynptr msg_ptr, sig_ptr; + int err =3D 0; + int ret; + + hash_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!hash_ctx) { + ecdsa_on_hash_ctx_status =3D 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 =3D 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 =3D (ret =3D=3D -22) ? 0 : ret; + bpf_crypto_ctx_release(hash_ctx); + return 0; +} + +/* Test that calling bpf_sig_keysize on hash context fails with type misma= tch */ +SEC("syscall") +int test_ecdsa_keysize_on_hash_ctx(void *ctx) +{ + struct bpf_crypto_ctx *hash_ctx; + struct bpf_crypto_params params =3D { + .type =3D "hash", + .algo =3D "sha256", + .key_len =3D 0, + }; + int err =3D 0; + int ret; + + hash_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!hash_ctx) { + ecdsa_keysize_on_hash_status =3D err; + return 0; + } + + ret =3D bpf_sig_keysize(hash_ctx); + /* Expected: should fail with -EINVAL (-22) due to type_id mismatch */ + ecdsa_keysize_on_hash_status =3D (ret =3D=3D -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 =3D { + .type =3D "sig", + .algo =3D "p1363(ecdsa-nist-p256)", + .key_len =3D sizeof(pubkey_p256), + }; + struct bpf_dynptr msg_ptr, sig_ptr; + int err =3D 0; + int ret; + + __builtin_memcpy(params.key, pubkey_p256, sizeof(pubkey_p256)); + + ecdsa_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!ecdsa_ctx) { + ecdsa_zero_msg_status =3D 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 =3D bpf_sig_verify(ecdsa_ctx, &msg_ptr, &sig_ptr); + /* Expected: should fail with -EINVAL (-22) */ + ecdsa_zero_msg_status =3D (ret =3D=3D -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 =3D { + .type =3D "sig", + .algo =3D "p1363(ecdsa-nist-p256)", + .key_len =3D sizeof(pubkey_p256), + }; + struct bpf_dynptr msg_ptr, sig_ptr; + int err =3D 0; + int ret; + + __builtin_memcpy(params.key, pubkey_p256, sizeof(pubkey_p256)); + + ecdsa_ctx =3D bpf_crypto_ctx_create(¶ms, sizeof(params), &err); + if (!ecdsa_ctx) { + ecdsa_zero_sig_status =3D 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 =3D bpf_sig_verify(ecdsa_ctx, &msg_ptr, &sig_ptr); + /* Expected: should fail with -EINVAL (-22) */ + ecdsa_zero_sig_status =3D (ret =3D=3D -22) ? 0 : ret; + bpf_crypto_ctx_release(ecdsa_ctx); + return 0; +} + +char __license[] SEC("license") =3D "GPL"; --=20 2.52.0