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) += 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..95c178ec0ce8 --- /dev/null +++ b/crypto/bpf_crypto_shash.c @@ -0,0 +1,95 @@ +// 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 = 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, + .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/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..138abe58e87e 100644 --- a/kernel/bpf/crypto.c +++ b/kernel/bpf/crypto.c @@ -9,6 +9,7 @@ #include #include #include +#include struct bpf_crypto_type_list { const struct bpf_crypto_type *type; @@ -57,6 +58,21 @@ struct bpf_crypto_ctx { refcount_t usage; }; +#if IS_ENABLED(CONFIG_CRYPTO_ECDSA) +/** + * struct bpf_ecdsa_ctx - refcounted BPF ECDSA context structure + * @tfm: The crypto_sig transform for ECDSA operations + * @rcu: The RCU head used to free the context with RCU safety + * @usage: Object reference counter. When the refcount goes to 0, the + * memory is released with RCU safety. + */ +struct bpf_ecdsa_ctx { + struct crypto_sig *tfm; + struct rcu_head rcu; + refcount_t usage; +}; +#endif + int bpf_crypto_register_type(const struct bpf_crypto_type *type) { struct bpf_crypto_type_list *node; @@ -171,7 +187,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 +216,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,12 +367,254 @@ __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; + u64 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); + + 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 */ + +#if IS_ENABLED(CONFIG_CRYPTO_ECDSA) +/** + * bpf_ecdsa_ctx_create() - Create a BPF ECDSA verification context + * @algo_name: bpf_dynptr to the algorithm name (e.g., "p1363(ecdsa-nist-p256)") + * @public_key: bpf_dynptr to the public key in uncompressed format (0x04 || x || y) + * Must be 65 bytes for P-256, 97 for P-384, 133 for P-521 + * @err: Pointer to store error code on failure + * + * Creates an ECDSA verification context that can be reused for multiple + * signature verifications. This function uses GFP_KERNEL allocation and + * can only be called from sleepable BPF programs. Uses bpf_dynptr to ensure + * safe memory access without risk of page faults. + */ +__bpf_kfunc struct bpf_ecdsa_ctx * +bpf_ecdsa_ctx_create(const struct bpf_dynptr *algo_name, + const struct bpf_dynptr *public_key, int *err) +{ + const struct bpf_dynptr_kern *algo_kern = (struct bpf_dynptr_kern *)algo_name; + const struct bpf_dynptr_kern *key_kern = (struct bpf_dynptr_kern *)public_key; + struct bpf_ecdsa_ctx *ctx; + const char *algo_ptr; + const u8 *key_ptr; + u32 algo_len, key_len; + char algo[64]; + int ret; + + if (!err) + return NULL; + + algo_len = __bpf_dynptr_size(algo_kern); + key_len = __bpf_dynptr_size(key_kern); + + if (algo_len == 0 || algo_len >= sizeof(algo)) { + *err = -EINVAL; + return NULL; + } + + if (key_len < 65) { + *err = -EINVAL; + return NULL; + } + + algo_ptr = __bpf_dynptr_data(algo_kern, algo_len); + if (!algo_ptr) { + *err = -EINVAL; + return NULL; + } + + key_ptr = __bpf_dynptr_data(key_kern, key_len); + if (!key_ptr) { + *err = -EINVAL; + return NULL; + } + + if (key_ptr[0] != 0x04) { + *err = -EINVAL; + return NULL; + } + + memcpy(algo, algo_ptr, algo_len); + algo[algo_len] = '\0'; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + *err = -ENOMEM; + return NULL; + } + + ctx->tfm = crypto_alloc_sig(algo, 0, 0); + if (IS_ERR(ctx->tfm)) { + *err = PTR_ERR(ctx->tfm); + kfree(ctx); + return NULL; + } + + ret = crypto_sig_set_pubkey(ctx->tfm, key_ptr, key_len); + if (ret) { + *err = ret; + crypto_free_sig(ctx->tfm); + kfree(ctx); + return NULL; + } + + refcount_set(&ctx->usage, 1); + *err = 0; + return ctx; +} + +/** + * bpf_ecdsa_verify() - Verify ECDSA signature using pre-allocated context + * @ctx: ECDSA context created by bpf_ecdsa_ctx_create() + * @message: bpf_dynptr to the message hash to verify. Must be a trusted pointer. + * @signature: bpf_dynptr to the ECDSA signature in r || s format. Must be a trusted pointer. + * Must be 64 bytes for P-256, 96 for P-384, 132 for P-521 + * + * Verifies an ECDSA signature using a pre-allocated context. This function + * does not allocate memory and can be used in non-sleepable BPF programs. + * Uses bpf_dynptr to ensure safe memory access without risk of page faults. + */ +__bpf_kfunc int bpf_ecdsa_verify(struct bpf_ecdsa_ctx *ctx, + const struct bpf_dynptr *message, + const struct bpf_dynptr *signature) +{ + const struct bpf_dynptr_kern *msg_kern = (struct bpf_dynptr_kern *)message; + const struct bpf_dynptr_kern *sig_kern = (struct bpf_dynptr_kern *)signature; + const u8 *msg_ptr, *sig_ptr; + u32 msg_len, sig_len; + + if (!ctx) + return -EINVAL; + + msg_len = __bpf_dynptr_size(msg_kern); + sig_len = __bpf_dynptr_size(sig_kern); + + if (msg_len == 0 || sig_len == 0) + return -EINVAL; + + msg_ptr = __bpf_dynptr_data(msg_kern, msg_len); + if (!msg_ptr) + return -EINVAL; + + sig_ptr = __bpf_dynptr_data(sig_kern, sig_len); + if (!sig_ptr) + return -EINVAL; + + return crypto_sig_verify(ctx->tfm, sig_ptr, sig_len, msg_ptr, msg_len); +} + +__bpf_kfunc struct bpf_ecdsa_ctx * +bpf_ecdsa_ctx_acquire(struct bpf_ecdsa_ctx *ctx) +{ + if (!refcount_inc_not_zero(&ctx->usage)) + return NULL; + return ctx; +} + +static void ecdsa_free_cb(struct rcu_head *head) +{ + struct bpf_ecdsa_ctx *ctx = container_of(head, struct bpf_ecdsa_ctx, rcu); + + crypto_free_sig(ctx->tfm); + kfree(ctx); +} + +__bpf_kfunc void bpf_ecdsa_ctx_release(struct bpf_ecdsa_ctx *ctx) +{ + if (refcount_dec_and_test(&ctx->usage)) + call_rcu(&ctx->rcu, ecdsa_free_cb); +} + +/** + * bpf_ecdsa_keysize() - Get the key size for ECDSA context + * @ctx: ECDSA context + * + * Returns: Key size in bits, or negative error code on failure + */ +__bpf_kfunc int bpf_ecdsa_keysize(struct bpf_ecdsa_ctx *ctx) +{ + if (!ctx) + return -EINVAL; + + return crypto_sig_keysize(ctx->tfm); +} + +/** + * bpf_ecdsa_digestsize() - Get the maximum digest size for ECDSA context + * @ctx: ECDSA context + */ +__bpf_kfunc int bpf_ecdsa_digestsize(struct bpf_ecdsa_ctx *ctx) +{ + if (!ctx) + return -EINVAL; + + return crypto_sig_digestsize(ctx->tfm); +} + +/** + * bpf_ecdsa_maxsize() - Get the maximum signature size for ECDSA context + * @ctx: ECDSA context + */ +__bpf_kfunc int bpf_ecdsa_maxsize(struct bpf_ecdsa_ctx *ctx) +{ + if (!ctx) + return -EINVAL; + + return crypto_sig_maxsize(ctx->tfm); +} +#endif /* CONFIG_CRYPTO_ECDSA */ + __bpf_kfunc_end_defs(); BTF_KFUNCS_START(crypt_init_kfunc_btf_ids) BTF_ID_FLAGS(func, bpf_crypto_ctx_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_crypto_ctx_release, KF_RELEASE) BTF_ID_FLAGS(func, bpf_crypto_ctx_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL) +#if IS_ENABLED(CONFIG_CRYPTO_ECDSA) +BTF_ID_FLAGS(func, bpf_ecdsa_ctx_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE) +BTF_ID_FLAGS(func, bpf_ecdsa_ctx_release, KF_RELEASE) +BTF_ID_FLAGS(func, bpf_ecdsa_ctx_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL) +#endif BTF_KFUNCS_END(crypt_init_kfunc_btf_ids) static const struct btf_kfunc_id_set crypt_init_kfunc_set = { @@ -359,6 +625,15 @@ 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 +#if IS_ENABLED(CONFIG_CRYPTO_ECDSA) +BTF_ID_FLAGS(func, bpf_ecdsa_verify, 0) +BTF_ID_FLAGS(func, bpf_ecdsa_keysize, 0) +BTF_ID_FLAGS(func, bpf_ecdsa_digestsize, 0) +BTF_ID_FLAGS(func, bpf_ecdsa_maxsize, 0) +#endif BTF_KFUNCS_END(crypt_kfunc_btf_ids) static const struct btf_kfunc_id_set crypt_kfunc_set = { @@ -369,6 +644,10 @@ static const struct btf_kfunc_id_set crypt_kfunc_set = { BTF_ID_LIST(bpf_crypto_dtor_ids) BTF_ID(struct, bpf_crypto_ctx) BTF_ID(func, bpf_crypto_ctx_release) +#if IS_ENABLED(CONFIG_CRYPTO_ECDSA) +BTF_ID(struct, bpf_ecdsa_ctx) +BTF_ID(func, bpf_ecdsa_ctx_release) +#endif static int __init crypto_kfunc_init(void) { @@ -378,13 +657,24 @@ static int __init crypto_kfunc_init(void) .btf_id = bpf_crypto_dtor_ids[0], .kfunc_btf_id = bpf_crypto_dtor_ids[1] }, +#if IS_ENABLED(CONFIG_CRYPTO_ECDSA) + { + .btf_id = bpf_crypto_dtor_ids[2], + .kfunc_btf_id = bpf_crypto_dtor_ids[3] + }, +#endif }; ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &crypt_kfunc_set); 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); + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, + &crypt_init_kfunc_set); + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, + &crypt_init_kfunc_set); return ret ?: register_btf_id_dtor_kfuncs(bpf_crypto_dtors, ARRAY_SIZE(bpf_crypto_dtors), THIS_MODULE); diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index 558839e3c185..c99811d3f61f 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -12,7 +12,10 @@ CONFIG_BPF_SYSCALL=y # CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set CONFIG_CGROUP_BPF=y CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_HASH2=y CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_ECDSA=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..1d8acbd56e32 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/crypto_hash.c @@ -0,0 +1,147 @@ +// 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] = { + 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); +} + +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(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/ecdsa_verify.c b/tools/testing/selftests/bpf/prog_tests/ecdsa_verify.c new file mode 100644 index 000000000000..4e88b3eeb3eb --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/ecdsa_verify.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ + +#include +#include "ecdsa_verify.skel.h" + +static void test_ecdsa_verify_valid_signature(void) +{ + struct ecdsa_verify *skel; + int err, prog_fd; + + LIBBPF_OPTS(bpf_test_run_opts, topts); + + skel = ecdsa_verify__open_and_load(); + if (!ASSERT_OK_PTR(skel, "ecdsa_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"); + + ecdsa_verify__destroy(skel); +} + +static void test_ecdsa_verify_invalid_signature(void) +{ + struct ecdsa_verify *skel; + int err, prog_fd; + + LIBBPF_OPTS(bpf_test_run_opts, topts); + + skel = ecdsa_verify__open_and_load(); + if (!ASSERT_OK_PTR(skel, "ecdsa_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"); + + ecdsa_verify__destroy(skel); +} + +static void test_ecdsa_size_queries(void) +{ + struct ecdsa_verify *skel; + int err, prog_fd; + + LIBBPF_OPTS(bpf_test_run_opts, topts); + + skel = ecdsa_verify__open_and_load(); + if (!ASSERT_OK_PTR(skel, "ecdsa_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->keysize_result, 256, "keysize_p256"); + ASSERT_EQ(skel->data->digestsize_result, 64, "digestsize_p256"); + ASSERT_EQ(skel->data->maxsize_result, 64, "maxsize_p256"); + + ecdsa_verify__destroy(skel); +} + +void test_ecdsa_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(); +} 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..0f9174b3cbd5 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/crypto_hash.c @@ -0,0 +1,142 @@ +// 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" + +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] = {}; + +int sha256_status = -1; +int sha384_status = -1; +int sha512_status = -1; + +/* Declare the crypto kfuncs */ +extern struct bpf_crypto_ctx *bpf_crypto_ctx_create(const struct bpf_crypto_params *params, + u32 params__sz, int *err) __ksym; +extern void bpf_crypto_ctx_release(struct bpf_crypto_ctx *ctx) __ksym; +extern int bpf_crypto_hash(struct bpf_crypto_ctx *ctx, const struct bpf_dynptr *data, + const struct bpf_dynptr *out) __ksym; + +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; +} + +char __license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/ecdsa_verify.c b/tools/testing/selftests/bpf/progs/ecdsa_verify.c new file mode 100644 index 000000000000..e2a94ff80774 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/ecdsa_verify.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include +#include "bpf_misc.h" + +struct bpf_ecdsa_ctx; +extern struct bpf_ecdsa_ctx * +bpf_ecdsa_ctx_create(const struct bpf_dynptr *algo_name, + const struct bpf_dynptr *public_key, int *err) __ksym; +extern int bpf_ecdsa_verify(struct bpf_ecdsa_ctx *ctx, + const struct bpf_dynptr *message, + const struct bpf_dynptr *signature) __ksym; +extern int bpf_ecdsa_keysize(struct bpf_ecdsa_ctx *ctx) __ksym; +extern int bpf_ecdsa_digestsize(struct bpf_ecdsa_ctx *ctx) __ksym; +extern int bpf_ecdsa_maxsize(struct bpf_ecdsa_ctx *ctx) __ksym; +extern void bpf_ecdsa_ctx_release(struct bpf_ecdsa_ctx *ctx) __ksym; + +/* NIST P-256 test vector + * This is a known valid ECDSA signature for testing purposes + */ + +/* Algorithm name for P-256 with p1363 format (standard r||s signature) */ +char algo_p256[] = "p1363(ecdsa-nist-p256)"; + +/* 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; + +SEC("syscall") +int test_ecdsa_verify_valid(void *ctx) +{ + struct bpf_ecdsa_ctx *ecdsa_ctx; + struct bpf_dynptr algo_ptr, key_ptr, msg_ptr, sig_ptr; + int err = 0; + + bpf_dynptr_from_mem(algo_p256, sizeof(algo_p256) - 1, 0, &algo_ptr); + bpf_dynptr_from_mem(pubkey_p256, sizeof(pubkey_p256), 0, &key_ptr); + + ecdsa_ctx = bpf_ecdsa_ctx_create(&algo_ptr, &key_ptr, &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_ecdsa_verify(ecdsa_ctx, &msg_ptr, &sig_ptr); + + bpf_ecdsa_ctx_release(ecdsa_ctx); + + return 0; +} + +SEC("syscall") +int test_ecdsa_verify_invalid(void *ctx) +{ + struct bpf_ecdsa_ctx *ecdsa_ctx; + struct bpf_dynptr algo_ptr, key_ptr, msg_ptr, sig_ptr; + int err = 0; + + bpf_dynptr_from_mem(algo_p256, sizeof(algo_p256) - 1, 0, &algo_ptr); + bpf_dynptr_from_mem(pubkey_p256, sizeof(pubkey_p256), 0, &key_ptr); + + ecdsa_ctx = bpf_ecdsa_ctx_create(&algo_ptr, &key_ptr, &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_ecdsa_verify(ecdsa_ctx, &msg_ptr, &sig_ptr); + + bpf_ecdsa_ctx_release(ecdsa_ctx); + + return 0; +} + +SEC("syscall") +int test_ecdsa_size_queries(void *ctx) +{ + struct bpf_ecdsa_ctx *ecdsa_ctx; + struct bpf_dynptr algo_ptr, key_ptr; + int err = 0; + + bpf_dynptr_from_mem(algo_p256, sizeof(algo_p256) - 1, 0, &algo_ptr); + bpf_dynptr_from_mem(pubkey_p256, sizeof(pubkey_p256), 0, &key_ptr); + + ecdsa_ctx = bpf_ecdsa_ctx_create(&algo_ptr, &key_ptr, &err); + if (!ecdsa_ctx) + return 0; + + keysize_result = bpf_ecdsa_keysize(ecdsa_ctx); + digestsize_result = bpf_ecdsa_digestsize(ecdsa_ctx); + maxsize_result = bpf_ecdsa_maxsize(ecdsa_ctx); + + bpf_ecdsa_ctx_release(ecdsa_ctx); + + return 0; +} + +char __license[] SEC("license") = "GPL";