@@ -22,7 +22,7 @@ struct ovpn_key_direction {
size_t nonce_tail_size; /* only needed for GCM modes */
};
-/* all info for a particular symmetric key (primary or secondary) */
+/* direct-key material for a primary or secondary slot */
struct ovpn_key_config {
enum ovpn_cipher_alg cipher_alg;
u8 key_id;
@@ -36,22 +36,26 @@ struct ovpn_peer_key_reset {
struct ovpn_key_config key;
};
+/* state for one concrete AEAD key direction */
+struct ovpn_key_ctx {
+ struct crypto_aead *tfm;
+ u8 implicit_iv[OVPN_NONCE_SIZE];
+ union {
+ struct ovpn_pktid_recv recv;
+ struct ovpn_pktid_xmit xmit;
+ } pid ____cacheline_aligned_in_smp;
+ struct ovpn_key_usage usage;
+ atomic64_t decrypt_failures;
+ unsigned long decrypt_failure_flags;
+};
+
struct ovpn_crypto_key_slot {
u8 key_id;
enum ovpn_cipher_alg cipher_alg;
struct ovpn_limit usage_limit;
- struct crypto_aead *encrypt;
- struct crypto_aead *decrypt;
- atomic64_t decrypt_failures;
- unsigned long decrypt_failure_flags;
- 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_key_usage usage_recv;
- struct ovpn_pktid_xmit pid_xmit ____cacheline_aligned_in_smp;
- struct ovpn_key_usage usage_xmit;
+ struct ovpn_key_ctx *encrypt;
+ struct ovpn_key_ctx *decrypt;
struct kref refcount;
struct rcu_head rcu;
};
@@ -29,24 +29,24 @@
#define OVPN_AEAD_DECRYPT_FAILURE_NOTIFY_BIT 0
static bool
-ovpn_aead_decrypt_failure_exceeded(const struct ovpn_crypto_key_slot *ks)
+ovpn_aead_decrypt_failure_exceeded(const struct ovpn_key_ctx *key)
{
- return atomic64_read(&ks->decrypt_failures) >
+ return atomic64_read(&key->decrypt_failures) >
OVPN_AEAD_DECRYPT_FAILURE_LIMIT;
}
-bool ovpn_aead_decrypt_failure_record(struct ovpn_crypto_key_slot *ks)
+bool ovpn_aead_decrypt_failure_record(struct ovpn_key_ctx *key)
{
- u64 failures = atomic64_inc_return(&ks->decrypt_failures);
+ u64 failures = atomic64_inc_return(&key->decrypt_failures);
return failures > OVPN_AEAD_DECRYPT_FAILURE_NOTIFY &&
!test_and_set_bit(OVPN_AEAD_DECRYPT_FAILURE_NOTIFY_BIT,
- &ks->decrypt_failure_flags);
+ &key->decrypt_failure_flags);
}
-static int ovpn_aead_encap_overhead(const struct ovpn_crypto_key_slot *ks)
+static int ovpn_aead_encap_overhead(const struct ovpn_key_ctx *key)
{
- return OVPN_AEAD_DIRECT_AAD_SIZE + crypto_aead_authsize(ks->encrypt);
+ return OVPN_AEAD_DIRECT_AAD_SIZE + crypto_aead_authsize(key->tfm);
}
/**
@@ -150,8 +150,8 @@ static struct scatterlist *ovpn_aead_crypto_req_sg(struct crypto_aead *aead,
int ovpn_aead_encrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
struct sk_buff *skb)
{
- const unsigned int tag_size = crypto_aead_authsize(ks->encrypt);
- unsigned int plaintext_len;
+ struct ovpn_key_ctx *key = ks->encrypt;
+ unsigned int plaintext_len, tag_size;
struct aead_request *req;
struct sk_buff *trailer;
struct scatterlist *sg;
@@ -164,6 +164,7 @@ int ovpn_aead_encrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
ovpn_skb_cb(skb)->peer = peer;
ovpn_skb_cb(skb)->ks = ks;
plaintext_len = skb->len;
+ tag_size = crypto_aead_authsize(key->tfm);
/* Sample AEAD header format:
* 48000001 00000005 7e7046bd 444a7e28 cc6387b1 64a4d6c1 380275a...
@@ -187,16 +188,16 @@ int ovpn_aead_encrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
return -ENOSPC;
/* allocate temporary memory for iv, sg and req */
- tmp = kmalloc(ovpn_aead_crypto_tmp_size(ks->encrypt, nfrags),
+ tmp = kmalloc(ovpn_aead_crypto_tmp_size(key->tfm, nfrags),
GFP_ATOMIC);
if (unlikely(!tmp))
return -ENOMEM;
ovpn_skb_cb(skb)->crypto_tmp = tmp;
- iv = ovpn_aead_crypto_tmp_iv(ks->encrypt, tmp);
- req = ovpn_aead_crypto_tmp_req(ks->encrypt, iv);
- sg = ovpn_aead_crypto_req_sg(ks->encrypt, req);
+ iv = ovpn_aead_crypto_tmp_iv(key->tfm, tmp);
+ req = ovpn_aead_crypto_tmp_req(key->tfm, iv);
+ sg = ovpn_aead_crypto_req_sg(key->tfm, req);
/* sg table:
* 0: op, wire nonce (AD, len=OVPN_AEAD_DIRECT_AAD_SIZE),
@@ -223,7 +224,7 @@ int ovpn_aead_encrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
aead_blocks = ovpn_aead_limit_blocks(ks->cipher_alg,
OVPN_AEAD_DIRECT_AAD_SIZE,
plaintext_len);
- ret = ovpn_pktid_xmit_next(&ks->pid_xmit, &ks->usage_xmit,
+ ret = ovpn_pktid_xmit_next(&key->pid.xmit, &key->usage,
&ks->usage_limit, aead_blocks, &pktid);
if (unlikely(ret < 0))
return ret;
@@ -233,7 +234,7 @@ int ovpn_aead_encrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
/* concat 4 bytes packet id and 8 bytes nonce tail into 12 bytes
* nonce
*/
- ovpn_pktid_aead_write(pktid, ks->nonce_tail_xmit, iv);
+ ovpn_pktid_aead_write(pktid, key->implicit_iv, iv);
/* make space for packet id and push it to the front */
__skb_push(skb, OVPN_NONCE_WIRE_SIZE);
@@ -249,10 +250,10 @@ int ovpn_aead_encrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
sg_set_buf(sg, skb->data, OVPN_AEAD_DIRECT_AAD_SIZE);
/* setup async crypto operation */
- aead_request_set_tfm(req, ks->encrypt);
+ aead_request_set_tfm(req, key->tfm);
aead_request_set_callback(req, 0, ovpn_encrypt_post, skb);
aead_request_set_crypt(req, sg, sg,
- skb->len - ovpn_aead_encap_overhead(ks), iv);
+ skb->len - ovpn_aead_encap_overhead(key), iv);
aead_request_set_ad(req, OVPN_AEAD_DIRECT_AAD_SIZE);
/* encrypt it */
@@ -262,15 +263,16 @@ 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)
{
- const unsigned int tag_size = crypto_aead_authsize(ks->decrypt);
+ struct ovpn_key_ctx *key = ks->decrypt;
+ unsigned int payload_offset, tag_size;
int ret, payload_len, nfrags;
- unsigned int payload_offset;
struct aead_request *req;
struct sk_buff *trailer;
struct scatterlist *sg;
void *tmp;
u8 *iv;
+ tag_size = crypto_aead_authsize(key->tfm);
payload_offset = ovpn_aead_direct_payload_offset(tag_size);
payload_len = skb->len - payload_offset;
@@ -282,7 +284,7 @@ int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
if (unlikely(payload_len < 0))
return -EINVAL;
- if (unlikely(ovpn_aead_decrypt_failure_exceeded(ks)))
+ if (unlikely(ovpn_aead_decrypt_failure_exceeded(key)))
return -EKEYREJECTED;
/* Prepare the skb data buffer to be accessed up until the auth tag.
@@ -301,16 +303,16 @@ int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
return -ENOSPC;
/* allocate temporary memory for iv, sg and req */
- tmp = kmalloc(ovpn_aead_crypto_tmp_size(ks->decrypt, nfrags),
+ tmp = kmalloc(ovpn_aead_crypto_tmp_size(key->tfm, nfrags),
GFP_ATOMIC);
if (unlikely(!tmp))
return -ENOMEM;
ovpn_skb_cb(skb)->crypto_tmp = tmp;
- iv = ovpn_aead_crypto_tmp_iv(ks->decrypt, tmp);
- req = ovpn_aead_crypto_tmp_req(ks->decrypt, iv);
- sg = ovpn_aead_crypto_req_sg(ks->decrypt, req);
+ iv = ovpn_aead_crypto_tmp_iv(key->tfm, tmp);
+ req = ovpn_aead_crypto_tmp_req(key->tfm, iv);
+ sg = ovpn_aead_crypto_req_sg(key->tfm, req);
/* sg table:
* 0: op, wire nonce (AD, len=OVPN_AEAD_DIRECT_AAD_SIZE),
@@ -336,11 +338,11 @@ int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
/* copy nonce into IV buffer */
memcpy(iv, ovpn_aead_direct_wire_nonce(skb), OVPN_NONCE_WIRE_SIZE);
- memcpy(iv + OVPN_NONCE_WIRE_SIZE, ks->nonce_tail_recv,
- OVPN_NONCE_TAIL_SIZE);
+ memcpy(iv + OVPN_NONCE_WIRE_SIZE,
+ key->implicit_iv + OVPN_NONCE_WIRE_SIZE, OVPN_NONCE_TAIL_SIZE);
/* setup async crypto operation */
- aead_request_set_tfm(req, ks->decrypt);
+ aead_request_set_tfm(req, key->tfm);
aead_request_set_callback(req, 0, ovpn_decrypt_post, skb);
aead_request_set_crypt(req, sg, sg, payload_len + tag_size, iv);
@@ -20,6 +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);
-bool ovpn_aead_decrypt_failure_record(struct ovpn_crypto_key_slot *ks);
+bool ovpn_aead_decrypt_failure_record(struct ovpn_key_ctx *key);
#endif /* _NET_OVPN_OVPNAEAD_H_ */
@@ -71,13 +71,65 @@ static struct crypto_aead *ovpn_aead_init(const char *title,
return ERR_PTR(ret);
}
+static void ovpn_key_ctx_free(struct ovpn_key_ctx *key)
+{
+ if (!key)
+ return;
+
+ if (key->tfm)
+ crypto_free_aead(key->tfm);
+ memzero_explicit(key->implicit_iv, sizeof(key->implicit_iv));
+ kfree(key);
+}
+
+static struct ovpn_key_ctx *
+ovpn_key_ctx_new(const char *title, const char *alg_name,
+ const struct ovpn_key_direction *dir, bool encrypt)
+{
+ struct ovpn_key_ctx *key;
+ size_t tail_offset;
+ int ret;
+
+ key = kmalloc_obj(*key);
+ if (!key)
+ return ERR_PTR(-ENOMEM);
+
+ /* create the concrete AEAD transform first */
+ key->tfm = ovpn_aead_init(title, alg_name, dir->cipher_key,
+ dir->cipher_key_size);
+ if (IS_ERR(key->tfm)) {
+ ret = PTR_ERR(key->tfm);
+ key->tfm = NULL;
+ ovpn_key_ctx_free(key);
+ return ERR_PTR(ret);
+ }
+
+ /* store the implicit IV in a full nonce-sized buffer */
+ tail_offset = OVPN_NONCE_SIZE - dir->nonce_tail_size;
+ memset(key->implicit_iv, 0, sizeof(key->implicit_iv));
+ memcpy(key->implicit_iv + tail_offset, dir->nonce_tail,
+ dir->nonce_tail_size);
+
+ ovpn_key_usage_init(&key->usage);
+ atomic64_set(&key->decrypt_failures, 0);
+ key->decrypt_failure_flags = 0;
+
+ /* initialize only the packet ID direction this context owns */
+ if (encrypt)
+ ovpn_pktid_xmit_init(&key->pid.xmit);
+ else
+ ovpn_pktid_recv_init(&key->pid.recv);
+
+ return key;
+}
+
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);
+ ovpn_key_ctx_free(ks->encrypt);
+ ovpn_key_ctx_free(ks->decrypt);
kfree(ks);
}
@@ -115,38 +167,23 @@ ovpn_crypto_key_slot_new(const struct ovpn_key_config *kc)
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);
+
+ ks->encrypt = ovpn_key_ctx_new("encrypt", alg_name, &kc->encrypt,
+ true);
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);
+ ks->decrypt = ovpn_key_ctx_new("decrypt", alg_name, &kc->decrypt,
+ false);
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:
@@ -158,10 +195,10 @@ enum ovpn_cipher_alg ovpn_crypto_key_slot_alg(struct ovpn_crypto_key_slot *ks)
{
const char *alg_name;
- if (!ks->encrypt)
+ if (!ks->encrypt || !ks->encrypt->tfm)
return OVPN_CIPHER_ALG_NONE;
- alg_name = crypto_tfm_alg_name(crypto_aead_tfm(ks->encrypt));
+ alg_name = crypto_tfm_alg_name(crypto_aead_tfm(ks->encrypt->tfm));
if (!strcmp(alg_name, ALG_NAME_AES))
return OVPN_CIPHER_ALG_AES_GCM;
@@ -111,6 +111,7 @@ void ovpn_decrypt_post(void *data, int ret)
unsigned int payload_offset = 0;
struct sk_buff *skb = data;
struct ovpn_socket *sock;
+ struct ovpn_key_ctx *key;
struct ovpn_peer *peer;
u64 aead_blocks;
__be16 proto;
@@ -124,13 +125,14 @@ void ovpn_decrypt_post(void *data, int ret)
payload_offset = ovpn_skb_cb(skb)->payload_offset;
ks = ovpn_skb_cb(skb)->ks;
+ key = ks->decrypt;
peer = ovpn_skb_cb(skb)->peer;
/* crypto is done, cleanup skb CB and its members */
kfree(ovpn_skb_cb(skb)->crypto_tmp);
if (unlikely(ret == -EBADMSG)) {
- if (unlikely(ovpn_aead_decrypt_failure_record(ks)))
+ if (unlikely(ovpn_aead_decrypt_failure_record(key)))
ovpn_nl_key_swap_notify(peer, ks->key_id);
goto drop;
}
@@ -139,7 +141,7 @@ void ovpn_decrypt_post(void *data, int ret)
goto drop;
pktid = ovpn_aead_direct_pktid(skb);
- ret = ovpn_pktid_recv(&ks->pid_recv, pktid, 0);
+ ret = ovpn_pktid_recv(&key->pid.recv, pktid, 0);
if (unlikely(ret < 0)) {
net_err_ratelimited("%s: PKT ID RX error for peer %u: %d\n",
netdev_name(peer->ovpn->dev), peer->id,
@@ -150,8 +152,7 @@ void ovpn_decrypt_post(void *data, int ret)
aead_blocks = ovpn_aead_limit_blocks(ks->cipher_alg,
OVPN_AEAD_DIRECT_AAD_SIZE,
skb->len - payload_offset);
- if (unlikely(ovpn_pktid_recv_update_aead(&ks->pid_recv,
- &ks->usage_recv,
+ if (unlikely(ovpn_pktid_recv_update_aead(&key->pid.recv, &key->usage,
&ks->usage_limit,
aead_blocks)))
ovpn_nl_key_swap_notify(peer, ks->key_id);
@@ -128,14 +128,16 @@ ovpn_pktid_recv_update_aead(struct ovpn_pktid_recv *pr,
return ret;
}
-/* Write 12-byte AEAD IV to dest */
+/* write the direct-key AEAD IV to dest */
static inline void ovpn_pktid_aead_write(const u32 pktid,
- const u8 nt[],
+ const u8 implicit_iv[],
unsigned char *dest)
{
*(__force __be32 *)(dest) = htonl(pktid);
- BUILD_BUG_ON(4 + OVPN_NONCE_TAIL_SIZE != OVPN_NONCE_SIZE);
- memcpy(dest + 4, nt, OVPN_NONCE_TAIL_SIZE);
+ BUILD_BUG_ON(OVPN_NONCE_WIRE_SIZE + OVPN_NONCE_TAIL_SIZE !=
+ OVPN_NONCE_SIZE);
+ memcpy(dest + OVPN_NONCE_WIRE_SIZE,
+ implicit_iv + OVPN_NONCE_WIRE_SIZE, OVPN_NONCE_TAIL_SIZE);
}
void ovpn_pktid_xmit_init(struct ovpn_pktid_xmit *pid);