From nobody Tue Dec 16 14:34:15 2025 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 06822213254; Fri, 5 Dec 2025 17:42:59 +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=1764956582; cv=none; b=mwhYoqNjLuhgKPIkPwKTlUKKm0HBvtCtszh5lXZwyGpvUO8Eb0SwKsbTYDgASU0HD6mlMavGnCZ4Td+5MkGGq/63NBPAdJkh3peEgaX8++N+/sdIqpN8yHucOiP+YK03IAdtMmuOJmgrLIqEKhfl9+BrUU6TDGT7VfxSDT2r/BQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764956582; c=relaxed/simple; bh=H6Qz8x1rSzpr6+849NT4w6mDG9ie4+0BXtUNgMHU0gk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XaiqxkTM/vujDPSC8nSZ2f3mQvr0qojhxwt0i4Gf8rQLw+rynDABLYisD+keT8ws4yIFhmWn17z1P34RVzhNyf8BVHae2h5QXCAL34DwHsAFQKX7EmMGGK8ppXF5RrzqfFxk7Zl/fbrCzjUYYDjj4f0MG6dIyNiI7AGfTMicVFk= 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=XcljczDz; dkim=permerror (0-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b=BrHd3TD0; 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="XcljczDz"; dkim=permerror (0-bit key) header.d=danielhodges.dev header.i=@danielhodges.dev header.b="BrHd3TD0" DKIM-Signature: v=1; a=rsa-sha256; s=202510r; d=danielhodges.dev; c=relaxed/relaxed; h=Message-ID:Date:Subject:To:From; t=1764956365; bh=se5LlHSBJ7lq7UVt+yUWy3C dL2n08J14Yo+VA/PYMoQ=; b=XcljczDzdHd4XxzKwi/djMpBwXZSQEACUYz78ZXXtxMR6/MYXm Z7xVDIaCqq+bNniGw2o69txb3m/FbSEL8mFnyleQXGBB/xRz0sTXTMuUJVnLVNjTs8v9piX+BSS +9zc4ZiAX4HBa+urlEYD/O0rJvIY6im5rL5kp/1wC29F4BCB3J7tskhz4rMs/ku0WyCd/qyj9p2 GSwPX3fhAnJ4iSi3iWOKsEvKBajK7P3VgMmeHYgz+LhtFcob41ZKx/ANTN063E6/PGOXDDiK+/b LLVrWO2yFZLevl3bCrlvI1xcfilblm97SB4oaAJhxKm1Sp5KlchZ7FjyQ8bYJMr5gJQ==; DKIM-Signature: v=1; a=ed25519-sha256; s=202510e; d=danielhodges.dev; c=relaxed/relaxed; h=Message-ID:Date:Subject:To:From; t=1764956365; bh=se5LlHSBJ7lq7UVt+yUWy3C dL2n08J14Yo+VA/PYMoQ=; b=BrHd3TD01cUr7p8NQHAQvBzEaXVvXCFOqs3VY8Isurl7hKKGG1 +TOo8gwc8ORC1I3iv3iMpTkeQ1eQLMWbPhCA==; From: Daniel Hodges To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, vadim.fedorenko@linux.dev Cc: martin.lau@linux.dev, eddyz87@gmail.com, song@kernel.org, yonghong.song@linux.dev, john.fastabend@gmail.com, kpsingh@kernel.org, sdf@fomichev.me, haoluo@google.com, jolsa@kernel.org, herbert@gondor.apana.org.au, davem@davemloft.net, shuah@kernel.org, bpf@vger.kernel.org, linux-crypto@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Daniel Hodges Subject: [PATCH bpf-next v2 4/5] bpf: Add ECDSA signature verification kfuncs Date: Fri, 5 Dec 2025 12:39:22 -0500 Message-ID: <20251205173923.31740-5-git@danielhodges.dev> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251205173923.31740-1-git@danielhodges.dev> References: <20251205173923.31740-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 context-based ECDSA signature verification kfuncs: - bpf_ecdsa_ctx_create(): Creates reusable ECDSA context with public key - bpf_ecdsa_verify(): Verifies signatures using the context - bpf_ecdsa_ctx_acquire(): Increments context reference count - bpf_ecdsa_ctx_release(): Releases context with RCU safety The ECDSA implementation supports NIST curves (P-256, P-384, P-521) and uses the kernel's crypto_sig API. Public keys must be in uncompressed format (0x04 || x || y), and signatures are in r || s format. Signed-off-by: Daniel Hodges --- kernel/bpf/crypto.c | 230 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) diff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c index 95625c7ffb1a..3a3741cf657f 100644 --- a/kernel/bpf/crypto.c +++ b/kernel/bpf/crypto.c @@ -9,6 +9,7 @@ #include #include #include +#include =20 struct bpf_crypto_type_list { const struct bpf_crypto_type *type; @@ -57,6 +58,21 @@ struct bpf_crypto_ctx { refcount_t usage; }; =20 +#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; @@ -399,12 +415,206 @@ __bpf_kfunc int bpf_crypto_hash(struct bpf_crypto_ct= x *ctx, } #endif /* CONFIG_CRYPTO_HASH2 */ =20 +#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-p= 256)") + * @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 ensu= re + * 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 =3D (struct bpf_dynptr_kern *)alg= o_name; + const struct bpf_dynptr_kern *key_kern =3D (struct bpf_dynptr_kern *)publ= ic_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 =3D __bpf_dynptr_size(algo_kern); + key_len =3D __bpf_dynptr_size(key_kern); + + if (algo_len =3D=3D 0 || algo_len >=3D sizeof(algo)) { + *err =3D -EINVAL; + return NULL; + } + + if (key_len < 65) { + *err =3D -EINVAL; + return NULL; + } + + algo_ptr =3D __bpf_dynptr_data(algo_kern, algo_len); + if (!algo_ptr) { + *err =3D -EINVAL; + return NULL; + } + + key_ptr =3D __bpf_dynptr_data(key_kern, key_len); + if (!key_ptr) { + *err =3D -EINVAL; + return NULL; + } + + if (key_ptr[0] !=3D 0x04) { + *err =3D -EINVAL; + return NULL; + } + + memcpy(algo, algo_ptr, algo_len); + algo[algo_len] =3D '\0'; + + ctx =3D kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + *err =3D -ENOMEM; + return NULL; + } + + ctx->tfm =3D crypto_alloc_sig(algo, 0, 0); + if (IS_ERR(ctx->tfm)) { + *err =3D PTR_ERR(ctx->tfm); + kfree(ctx); + return NULL; + } + + ret =3D crypto_sig_set_pubkey(ctx->tfm, key_ptr, key_len); + if (ret) { + *err =3D ret; + crypto_free_sig(ctx->tfm); + kfree(ctx); + return NULL; + } + + refcount_set(&ctx->usage, 1); + *err =3D 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 p= ointer. + * @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 fault= s. + */ +__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 =3D (struct bpf_dynptr_kern *)mess= age; + const struct bpf_dynptr_kern *sig_kern =3D (struct bpf_dynptr_kern *)sign= ature; + const u8 *msg_ptr, *sig_ptr; + u32 msg_len, sig_len; + + if (!ctx) + return -EINVAL; + + msg_len =3D __bpf_dynptr_size(msg_kern); + sig_len =3D __bpf_dynptr_size(sig_kern); + + if (msg_len =3D=3D 0 || sig_len =3D=3D 0) + 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 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 =3D container_of(head, struct bpf_ecdsa_ctx, rc= u); + + 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(); =20 BTF_KFUNCS_START(crypt_init_kfunc_btf_ids) BTF_ID_FLAGS(func, bpf_crypto_ctx_create, KF_ACQUIRE | KF_RET_NULL | KF_SL= EEPABLE) BTF_ID_FLAGS(func, bpf_crypto_ctx_release, KF_RELEASE) BTF_ID_FLAGS(func, bpf_crypto_ctx_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NU= LL) +#if IS_ENABLED(CONFIG_CRYPTO_ECDSA) +BTF_ID_FLAGS(func, bpf_ecdsa_ctx_create, KF_ACQUIRE | KF_RET_NULL | KF_SLE= EPABLE) +BTF_ID_FLAGS(func, bpf_ecdsa_ctx_release, KF_RELEASE) +BTF_ID_FLAGS(func, bpf_ecdsa_ctx_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NUL= L) +#endif BTF_KFUNCS_END(crypt_init_kfunc_btf_ids) =20 static const struct btf_kfunc_id_set crypt_init_kfunc_set =3D { @@ -418,6 +628,12 @@ BTF_ID_FLAGS(func, bpf_crypto_encrypt, KF_RCU) #if IS_ENABLED(CONFIG_CRYPTO_HASH2) BTF_ID_FLAGS(func, bpf_crypto_hash, KF_RCU) #endif +#if IS_ENABLED(CONFIG_CRYPTO_ECDSA) +BTF_ID_FLAGS(func, bpf_ecdsa_verify, 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) =20 static const struct btf_kfunc_id_set crypt_kfunc_set =3D { @@ -428,6 +644,10 @@ static const struct btf_kfunc_id_set crypt_kfunc_set = =3D { 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 =20 static int __init crypto_kfunc_init(void) { @@ -437,6 +657,12 @@ static int __init crypto_kfunc_init(void) .btf_id =3D bpf_crypto_dtor_ids[0], .kfunc_btf_id =3D bpf_crypto_dtor_ids[1] }, +#if IS_ENABLED(CONFIG_CRYPTO_ECDSA) + { + .btf_id =3D bpf_crypto_dtor_ids[2], + .kfunc_btf_id =3D bpf_crypto_dtor_ids[3] + }, +#endif }; =20 ret =3D register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &crypt_kfunc_s= et); @@ -445,6 +671,10 @@ static int __init crypto_kfunc_init(void) 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); + ret =3D ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, + &crypt_init_kfunc_set); + ret =3D 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); --=20 2.51.0