@@ -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
@@ -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();
@@ -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);
@@ -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;
-}
@@ -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_ */
new file mode 100644
@@ -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;
+}