2025-04-15 13:17:26 +02:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
|
|
/* OpenVPN data channel offload
|
|
|
|
*
|
|
|
|
* Copyright (C) 2020-2025 OpenVPN, Inc.
|
|
|
|
*
|
|
|
|
* Author: James Yonan <james@openvpn.net>
|
|
|
|
* Antonio Quartulli <antonio@openvpn.net>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _NET_OVPN_OVPNCRYPTO_H_
|
|
|
|
#define _NET_OVPN_OVPNCRYPTO_H_
|
|
|
|
|
|
|
|
#include "pktid.h"
|
|
|
|
#include "proto.h"
|
|
|
|
|
|
|
|
/* info needed for both encrypt and decrypt directions */
|
|
|
|
struct ovpn_key_direction {
|
|
|
|
const u8 *cipher_key;
|
|
|
|
size_t cipher_key_size;
|
|
|
|
const u8 *nonce_tail; /* only needed for GCM modes */
|
|
|
|
size_t nonce_tail_size; /* only needed for GCM modes */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* all info for a particular symmetric key (primary or secondary) */
|
|
|
|
struct ovpn_key_config {
|
|
|
|
enum ovpn_cipher_alg cipher_alg;
|
|
|
|
u8 key_id;
|
|
|
|
struct ovpn_key_direction encrypt;
|
|
|
|
struct ovpn_key_direction decrypt;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* used to pass settings from netlink to the crypto engine */
|
|
|
|
struct ovpn_peer_key_reset {
|
|
|
|
enum ovpn_key_slot slot;
|
|
|
|
struct ovpn_key_config key;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ovpn_crypto_key_slot {
|
|
|
|
u8 key_id;
|
|
|
|
|
|
|
|
struct crypto_aead *encrypt;
|
|
|
|
struct crypto_aead *decrypt;
|
|
|
|
u8 nonce_tail_xmit[OVPN_NONCE_TAIL_SIZE];
|
|
|
|
u8 nonce_tail_recv[OVPN_NONCE_TAIL_SIZE];
|
|
|
|
|
|
|
|
struct ovpn_pktid_recv pid_recv ____cacheline_aligned_in_smp;
|
|
|
|
struct ovpn_pktid_xmit pid_xmit ____cacheline_aligned_in_smp;
|
|
|
|
struct kref refcount;
|
|
|
|
struct rcu_head rcu;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ovpn_crypto_state {
|
|
|
|
struct ovpn_crypto_key_slot __rcu *slots[2];
|
|
|
|
u8 primary_idx;
|
|
|
|
|
|
|
|
/* protects primary and secondary slots */
|
|
|
|
spinlock_t lock;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline bool ovpn_crypto_key_slot_hold(struct ovpn_crypto_key_slot *ks)
|
|
|
|
{
|
|
|
|
return kref_get_unless_zero(&ks->refcount);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void ovpn_crypto_state_init(struct ovpn_crypto_state *cs)
|
|
|
|
{
|
|
|
|
RCU_INIT_POINTER(cs->slots[0], NULL);
|
|
|
|
RCU_INIT_POINTER(cs->slots[1], NULL);
|
|
|
|
cs->primary_idx = 0;
|
|
|
|
spin_lock_init(&cs->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct ovpn_crypto_key_slot *
|
|
|
|
ovpn_crypto_key_id_to_slot(const struct ovpn_crypto_state *cs, u8 key_id)
|
|
|
|
{
|
|
|
|
struct ovpn_crypto_key_slot *ks;
|
|
|
|
u8 idx;
|
|
|
|
|
|
|
|
if (unlikely(!cs))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
idx = READ_ONCE(cs->primary_idx);
|
|
|
|
ks = rcu_dereference(cs->slots[idx]);
|
|
|
|
if (ks && ks->key_id == key_id) {
|
|
|
|
if (unlikely(!ovpn_crypto_key_slot_hold(ks)))
|
|
|
|
ks = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ks = rcu_dereference(cs->slots[!idx]);
|
|
|
|
if (ks && ks->key_id == key_id) {
|
|
|
|
if (unlikely(!ovpn_crypto_key_slot_hold(ks)))
|
|
|
|
ks = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* when both key slots are occupied but no matching key ID is found, ks
|
|
|
|
* has to be reset to NULL to avoid carrying a stale pointer
|
|
|
|
*/
|
|
|
|
ks = NULL;
|
|
|
|
out:
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
return ks;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct ovpn_crypto_key_slot *
|
|
|
|
ovpn_crypto_key_slot_primary(const struct ovpn_crypto_state *cs)
|
|
|
|
{
|
|
|
|
struct ovpn_crypto_key_slot *ks;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
ks = rcu_dereference(cs->slots[cs->primary_idx]);
|
|
|
|
if (unlikely(ks && !ovpn_crypto_key_slot_hold(ks)))
|
|
|
|
ks = NULL;
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
return ks;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ovpn_crypto_key_slot_release(struct kref *kref);
|
|
|
|
|
|
|
|
static inline void ovpn_crypto_key_slot_put(struct ovpn_crypto_key_slot *ks)
|
|
|
|
{
|
|
|
|
kref_put(&ks->refcount, ovpn_crypto_key_slot_release);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ovpn_crypto_state_reset(struct ovpn_crypto_state *cs,
|
|
|
|
const struct ovpn_peer_key_reset *pkr);
|
|
|
|
|
|
|
|
void ovpn_crypto_key_slot_delete(struct ovpn_crypto_state *cs,
|
|
|
|
enum ovpn_key_slot slot);
|
|
|
|
|
|
|
|
void ovpn_crypto_state_release(struct ovpn_crypto_state *cs);
|
|
|
|
|
|
|
|
void ovpn_crypto_key_slots_swap(struct ovpn_crypto_state *cs);
|
|
|
|
|
2025-04-15 13:17:36 +02:00
|
|
|
int ovpn_crypto_config_get(struct ovpn_crypto_state *cs,
|
|
|
|
enum ovpn_key_slot slot,
|
|
|
|
struct ovpn_key_config *keyconf);
|
|
|
|
|
2025-04-15 13:17:37 +02:00
|
|
|
bool ovpn_crypto_kill_key(struct ovpn_crypto_state *cs, u8 key_id);
|
|
|
|
|
2025-04-15 13:17:26 +02:00
|
|
|
#endif /* _NET_OVPN_OVPNCRYPTO_H_ */
|