[Openvpn-devel,RFC,net-next,05/14] ovpn: move key slot lifecycle out of AEAD

Message ID 1a0ef417303069c983fc8eb9effcede54c231e26.1782919654.git.ralf@mandelbit.com
State Superseded
Headers
Series ovpn: add epoch data channel keys |

Commit Message

Ralf Lici July 1, 2026, 3:43 p.m. UTC
  crypto_aead.c now contains both AEAD packet operations and key slot
setup, destruction and cipher introspection. Those lifecycle helpers are
used by the generic crypto state code and do not need to live with the
per-packet encrypt and decrypt path.

Move key slot lifecycle code into crypto_key.c and expose it through
ovpn_crypto_key_slot helpers. This leaves crypto_aead.c focused on AEAD
packet processing without changing key allocation, validation or
teardown behavior.

Signed-off-by: Ralf Lici <ralf@mandelbit.com>
---
 drivers/net/ovpn/Makefile      |   1 +
 drivers/net/ovpn/crypto.c      |   7 +-
 drivers/net/ovpn/crypto.h      |   7 ++
 drivers/net/ovpn/crypto_aead.c | 158 ------------------------------
 drivers/net/ovpn/crypto_aead.h |   5 -
 drivers/net/ovpn/crypto_key.c  | 172 +++++++++++++++++++++++++++++++++
 6 files changed, 183 insertions(+), 167 deletions(-)
 create mode 100644 drivers/net/ovpn/crypto_key.c
  

Patch

diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile
index 229be66167e1..704af1deb7c1 100644
--- a/drivers/net/ovpn/Makefile
+++ b/drivers/net/ovpn/Makefile
@@ -10,6 +10,7 @@  obj-$(CONFIG_OVPN) := ovpn.o
 ovpn-y += bind.o
 ovpn-y += crypto.o
 ovpn-y += crypto_aead.o
+ovpn-y += crypto_key.o
 ovpn-y += main.o
 ovpn-y += io.o
 ovpn-y += netlink.o
diff --git a/drivers/net/ovpn/crypto.c b/drivers/net/ovpn/crypto.c
index 90580e32052f..239d95492574 100644
--- a/drivers/net/ovpn/crypto.c
+++ b/drivers/net/ovpn/crypto.c
@@ -15,7 +15,6 @@ 
 #include "ovpnpriv.h"
 #include "main.h"
 #include "pktid.h"
-#include "crypto_aead.h"
 #include "crypto.h"
 
 static void ovpn_ks_destroy_rcu(struct rcu_head *head)
@@ -23,7 +22,7 @@  static void ovpn_ks_destroy_rcu(struct rcu_head *head)
 	struct ovpn_crypto_key_slot *ks;
 
 	ks = container_of(head, struct ovpn_crypto_key_slot, rcu);
-	ovpn_aead_crypto_key_slot_destroy(ks);
+	ovpn_crypto_key_slot_destroy(ks);
 }
 
 void ovpn_crypto_key_slot_release(struct kref *kref)
@@ -89,7 +88,7 @@  int ovpn_crypto_state_reset(struct ovpn_crypto_state *cs,
 	    pkr->slot != OVPN_KEY_SLOT_SECONDARY)
 		return -EINVAL;
 
-	new = ovpn_aead_crypto_key_slot_new(&pkr->key);
+	new = ovpn_crypto_key_slot_new(&pkr->key);
 	if (IS_ERR(new))
 		return PTR_ERR(new);
 
@@ -202,7 +201,7 @@  int ovpn_crypto_config_get(struct ovpn_crypto_state *cs,
 		return -ENOENT;
 	}
 
-	keyconf->cipher_alg = ovpn_aead_crypto_alg(ks);
+	keyconf->cipher_alg = ovpn_crypto_key_slot_alg(ks);
 	keyconf->key_id = ks->key_id;
 	rcu_read_unlock();
 
diff --git a/drivers/net/ovpn/crypto.h b/drivers/net/ovpn/crypto.h
index 2be7ff54fc40..3b21b42a25eb 100644
--- a/drivers/net/ovpn/crypto.h
+++ b/drivers/net/ovpn/crypto.h
@@ -136,6 +136,13 @@  static inline void ovpn_crypto_key_slot_put(struct ovpn_crypto_key_slot *ks)
 int ovpn_crypto_state_reset(struct ovpn_crypto_state *cs,
 			    const struct ovpn_peer_key_reset *pkr);
 
+struct ovpn_crypto_key_slot *
+ovpn_crypto_key_slot_new(const struct ovpn_key_config *kc);
+
+void ovpn_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks);
+
+enum ovpn_cipher_alg ovpn_crypto_key_slot_alg(struct ovpn_crypto_key_slot *ks);
+
 void ovpn_crypto_key_slot_delete(struct ovpn_crypto_state *cs,
 				 enum ovpn_key_slot slot);
 
diff --git a/drivers/net/ovpn/crypto_aead.c b/drivers/net/ovpn/crypto_aead.c
index d2e3532934fb..5306c0d2b5c8 100644
--- a/drivers/net/ovpn/crypto_aead.c
+++ b/drivers/net/ovpn/crypto_aead.c
@@ -24,8 +24,6 @@ 
 #include "proto.h"
 #include "skb.h"
 
-#define ALG_NAME_AES		"gcm(aes)"
-#define ALG_NAME_CHACHAPOLY	"rfc7539(chacha20,poly1305)"
 #define OVPN_AEAD_DECRYPT_FAILURE_NOTIFY	BIT_ULL(35)
 #define OVPN_AEAD_DECRYPT_FAILURE_LIMIT		BIT_ULL(36)
 #define OVPN_AEAD_DECRYPT_FAILURE_NOTIFY_BIT	0
@@ -351,159 +349,3 @@  int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
 	/* decrypt it */
 	return crypto_aead_decrypt(req);
 }
-
-/* Initialize a struct crypto_aead object */
-static struct crypto_aead *ovpn_aead_init(const char *title,
-					  const char *alg_name,
-					  const unsigned char *key,
-					  unsigned int keylen)
-{
-	struct crypto_aead *aead;
-	int ret;
-
-	aead = crypto_alloc_aead(alg_name, 0, 0);
-	if (IS_ERR(aead)) {
-		ret = PTR_ERR(aead);
-		pr_err("%s crypto_alloc_aead failed, err=%d\n", title, ret);
-		aead = NULL;
-		goto error;
-	}
-
-	ret = crypto_aead_setkey(aead, key, keylen);
-	if (ret) {
-		pr_err("%s crypto_aead_setkey size=%u failed, err=%d\n", title,
-		       keylen, ret);
-		goto error;
-	}
-
-	ret = crypto_aead_setauthsize(aead, OVPN_AEAD_TAG_SIZE);
-	if (ret) {
-		pr_err("%s crypto_aead_setauthsize failed, err=%d\n", title,
-		       ret);
-		goto error;
-	}
-
-	/* basic AEAD assumption
-	 * all current algorithms use OVPN_NONCE_SIZE.
-	 * ovpn_aead_crypto_tmp_size and ovpn_aead_encrypt/decrypt
-	 * expect this.
-	 */
-	if (crypto_aead_ivsize(aead) != OVPN_NONCE_SIZE) {
-		pr_err("%s IV size must be %d\n", title, OVPN_NONCE_SIZE);
-		ret = -EINVAL;
-		goto error;
-	}
-
-	pr_debug("********* Cipher %s (%s)\n", alg_name, title);
-	pr_debug("*** IV size=%u\n", crypto_aead_ivsize(aead));
-	pr_debug("*** req size=%u\n", crypto_aead_reqsize(aead));
-	pr_debug("*** block size=%u\n", crypto_aead_blocksize(aead));
-	pr_debug("*** auth size=%u\n", crypto_aead_authsize(aead));
-	pr_debug("*** alignmask=0x%x\n", crypto_aead_alignmask(aead));
-
-	return aead;
-
-error:
-	crypto_free_aead(aead);
-	return ERR_PTR(ret);
-}
-
-void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks)
-{
-	if (!ks)
-		return;
-
-	crypto_free_aead(ks->encrypt);
-	crypto_free_aead(ks->decrypt);
-	kfree(ks);
-}
-
-struct ovpn_crypto_key_slot *
-ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc)
-{
-	struct ovpn_crypto_key_slot *ks = NULL;
-	const char *alg_name;
-	int ret;
-
-	/* validate crypto alg */
-	switch (kc->cipher_alg) {
-	case OVPN_CIPHER_ALG_AES_GCM:
-		alg_name = ALG_NAME_AES;
-		break;
-	case OVPN_CIPHER_ALG_CHACHA20_POLY1305:
-		alg_name = ALG_NAME_CHACHAPOLY;
-		break;
-	default:
-		return ERR_PTR(-EOPNOTSUPP);
-	}
-
-	if (kc->encrypt.nonce_tail_size != OVPN_NONCE_TAIL_SIZE ||
-	    kc->decrypt.nonce_tail_size != OVPN_NONCE_TAIL_SIZE)
-		return ERR_PTR(-EINVAL);
-
-	/* build the key slot */
-	ks = kmalloc_obj(*ks);
-	if (!ks)
-		return ERR_PTR(-ENOMEM);
-
-	ks->encrypt = NULL;
-	ks->decrypt = NULL;
-	kref_init(&ks->refcount);
-	ks->key_id = kc->key_id;
-	ks->cipher_alg = kc->cipher_alg;
-	ovpn_key_usage_limit_init(&ks->usage_limit, kc->cipher_alg);
-	ovpn_key_usage_init(&ks->usage_xmit);
-	ovpn_key_usage_init(&ks->usage_recv);
-	atomic64_set(&ks->decrypt_failures, 0);
-	ks->decrypt_failure_flags = 0;
-
-	ks->encrypt = ovpn_aead_init("encrypt", alg_name,
-				     kc->encrypt.cipher_key,
-				     kc->encrypt.cipher_key_size);
-	if (IS_ERR(ks->encrypt)) {
-		ret = PTR_ERR(ks->encrypt);
-		ks->encrypt = NULL;
-		goto destroy_ks;
-	}
-
-	ks->decrypt = ovpn_aead_init("decrypt", alg_name,
-				     kc->decrypt.cipher_key,
-				     kc->decrypt.cipher_key_size);
-	if (IS_ERR(ks->decrypt)) {
-		ret = PTR_ERR(ks->decrypt);
-		ks->decrypt = NULL;
-		goto destroy_ks;
-	}
-
-	memcpy(ks->nonce_tail_xmit, kc->encrypt.nonce_tail,
-	       OVPN_NONCE_TAIL_SIZE);
-	memcpy(ks->nonce_tail_recv, kc->decrypt.nonce_tail,
-	       OVPN_NONCE_TAIL_SIZE);
-
-	/* init packet ID generation/validation */
-	ovpn_pktid_xmit_init(&ks->pid_xmit);
-	ovpn_pktid_recv_init(&ks->pid_recv);
-
-	return ks;
-
-destroy_ks:
-	ovpn_aead_crypto_key_slot_destroy(ks);
-	return ERR_PTR(ret);
-}
-
-enum ovpn_cipher_alg ovpn_aead_crypto_alg(struct ovpn_crypto_key_slot *ks)
-{
-	const char *alg_name;
-
-	if (!ks->encrypt)
-		return OVPN_CIPHER_ALG_NONE;
-
-	alg_name = crypto_tfm_alg_name(crypto_aead_tfm(ks->encrypt));
-
-	if (!strcmp(alg_name, ALG_NAME_AES))
-		return OVPN_CIPHER_ALG_AES_GCM;
-	else if (!strcmp(alg_name, ALG_NAME_CHACHAPOLY))
-		return OVPN_CIPHER_ALG_CHACHA20_POLY1305;
-	else
-		return OVPN_CIPHER_ALG_NONE;
-}
diff --git a/drivers/net/ovpn/crypto_aead.h b/drivers/net/ovpn/crypto_aead.h
index e5ff0b9b5ee3..57a7b88cd6c5 100644
--- a/drivers/net/ovpn/crypto_aead.h
+++ b/drivers/net/ovpn/crypto_aead.h
@@ -20,11 +20,6 @@  int ovpn_aead_encrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
 int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
 		      struct sk_buff *skb);
 
-struct ovpn_crypto_key_slot *
-ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc);
-void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks);
-
-enum ovpn_cipher_alg ovpn_aead_crypto_alg(struct ovpn_crypto_key_slot *ks);
 bool ovpn_aead_decrypt_failure_record(struct ovpn_crypto_key_slot *ks);
 
 #endif /* _NET_OVPN_OVPNAEAD_H_ */
diff --git a/drivers/net/ovpn/crypto_key.c b/drivers/net/ovpn/crypto_key.c
new file mode 100644
index 000000000000..08fce700811f
--- /dev/null
+++ b/drivers/net/ovpn/crypto_key.c
@@ -0,0 +1,172 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*  OpenVPN data channel offload
+ *
+ *  Copyright (C) 2026 OpenVPN, Inc.
+ *
+ *  Author:	Ralf Lici <ralf@mandelbit.com>
+ *		Antonio Quartulli <antonio@openvpn.net>
+ */
+
+#include <crypto/aead.h>
+
+#include "ovpnpriv.h"
+#include "crypto.h"
+#include "pktid.h"
+#include "proto.h"
+
+#define ALG_NAME_AES		"gcm(aes)"
+#define ALG_NAME_CHACHAPOLY	"rfc7539(chacha20,poly1305)"
+
+/* initialize a struct crypto_aead object */
+static struct crypto_aead *ovpn_aead_init(const char *title,
+					  const char *alg_name,
+					  const unsigned char *key,
+					  unsigned int keylen)
+{
+	struct crypto_aead *aead;
+	int ret;
+
+	aead = crypto_alloc_aead(alg_name, 0, 0);
+	if (IS_ERR(aead)) {
+		ret = PTR_ERR(aead);
+		pr_err("%s crypto_alloc_aead failed, err=%d\n", title, ret);
+		aead = NULL;
+		goto error;
+	}
+
+	ret = crypto_aead_setkey(aead, key, keylen);
+	if (ret) {
+		pr_err("%s crypto_aead_setkey size=%u failed, err=%d\n", title,
+		       keylen, ret);
+		goto error;
+	}
+
+	ret = crypto_aead_setauthsize(aead, OVPN_AEAD_TAG_SIZE);
+	if (ret) {
+		pr_err("%s crypto_aead_setauthsize failed, err=%d\n", title,
+		       ret);
+		goto error;
+	}
+
+	/* Basic AEAD assumption: all current algorithms use OVPN_NONCE_SIZE.
+	 * ovpn_aead_crypto_tmp_size and ovpn_aead_encrypt/decrypt expect this.
+	 */
+	if (crypto_aead_ivsize(aead) != OVPN_NONCE_SIZE) {
+		pr_err("%s IV size must be %d\n", title, OVPN_NONCE_SIZE);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	pr_debug("********* Cipher %s (%s)\n", alg_name, title);
+	pr_debug("*** IV size=%u\n", crypto_aead_ivsize(aead));
+	pr_debug("*** req size=%u\n", crypto_aead_reqsize(aead));
+	pr_debug("*** block size=%u\n", crypto_aead_blocksize(aead));
+	pr_debug("*** auth size=%u\n", crypto_aead_authsize(aead));
+	pr_debug("*** alignmask=0x%x\n", crypto_aead_alignmask(aead));
+
+	return aead;
+
+error:
+	crypto_free_aead(aead);
+	return ERR_PTR(ret);
+}
+
+void ovpn_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks)
+{
+	if (!ks)
+		return;
+
+	crypto_free_aead(ks->encrypt);
+	crypto_free_aead(ks->decrypt);
+	kfree(ks);
+}
+
+struct ovpn_crypto_key_slot *
+ovpn_crypto_key_slot_new(const struct ovpn_key_config *kc)
+{
+	struct ovpn_crypto_key_slot *ks = NULL;
+	const char *alg_name;
+	int ret;
+
+	/* validate crypto alg */
+	switch (kc->cipher_alg) {
+	case OVPN_CIPHER_ALG_AES_GCM:
+		alg_name = ALG_NAME_AES;
+		break;
+	case OVPN_CIPHER_ALG_CHACHA20_POLY1305:
+		alg_name = ALG_NAME_CHACHAPOLY;
+		break;
+	default:
+		return ERR_PTR(-EOPNOTSUPP);
+	}
+
+	if (kc->encrypt.nonce_tail_size != OVPN_NONCE_TAIL_SIZE ||
+	    kc->decrypt.nonce_tail_size != OVPN_NONCE_TAIL_SIZE)
+		return ERR_PTR(-EINVAL);
+
+	/* build the key slot */
+	ks = kmalloc_obj(*ks);
+	if (!ks)
+		return ERR_PTR(-ENOMEM);
+
+	ks->encrypt = NULL;
+	ks->decrypt = NULL;
+	kref_init(&ks->refcount);
+	ks->key_id = kc->key_id;
+	ks->cipher_alg = kc->cipher_alg;
+	ovpn_key_usage_limit_init(&ks->usage_limit, kc->cipher_alg);
+	ovpn_key_usage_init(&ks->usage_xmit);
+	ovpn_key_usage_init(&ks->usage_recv);
+	atomic64_set(&ks->decrypt_failures, 0);
+	ks->decrypt_failure_flags = 0;
+
+	ks->encrypt = ovpn_aead_init("encrypt", alg_name,
+				     kc->encrypt.cipher_key,
+				     kc->encrypt.cipher_key_size);
+	if (IS_ERR(ks->encrypt)) {
+		ret = PTR_ERR(ks->encrypt);
+		ks->encrypt = NULL;
+		goto destroy_ks;
+	}
+
+	ks->decrypt = ovpn_aead_init("decrypt", alg_name,
+				     kc->decrypt.cipher_key,
+				     kc->decrypt.cipher_key_size);
+	if (IS_ERR(ks->decrypt)) {
+		ret = PTR_ERR(ks->decrypt);
+		ks->decrypt = NULL;
+		goto destroy_ks;
+	}
+
+	memcpy(ks->nonce_tail_xmit, kc->encrypt.nonce_tail,
+	       OVPN_NONCE_TAIL_SIZE);
+	memcpy(ks->nonce_tail_recv, kc->decrypt.nonce_tail,
+	       OVPN_NONCE_TAIL_SIZE);
+
+	/* init packet ID generation/validation */
+	ovpn_pktid_xmit_init(&ks->pid_xmit);
+	ovpn_pktid_recv_init(&ks->pid_recv);
+
+	return ks;
+
+destroy_ks:
+	ovpn_crypto_key_slot_destroy(ks);
+	return ERR_PTR(ret);
+}
+
+enum ovpn_cipher_alg ovpn_crypto_key_slot_alg(struct ovpn_crypto_key_slot *ks)
+{
+	const char *alg_name;
+
+	if (!ks->encrypt)
+		return OVPN_CIPHER_ALG_NONE;
+
+	alg_name = crypto_tfm_alg_name(crypto_aead_tfm(ks->encrypt));
+
+	if (!strcmp(alg_name, ALG_NAME_AES))
+		return OVPN_CIPHER_ALG_AES_GCM;
+	else if (!strcmp(alg_name, ALG_NAME_CHACHAPOLY))
+		return OVPN_CIPHER_ALG_CHACHA20_POLY1305;
+	else
+		return OVPN_CIPHER_ALG_NONE;
+}