Key slots currently mix slot-wide state with per-direction runtime state. That includes AEAD transforms, implicit IV material, packet ID state, usage accounting and decrypt failure tracking.
Move those per-direction pieces into struct ovpn_key_ctx. Keep the slot focused on key id, cipher selection and shared usage limits. This gives directly installed keys a cleaner state boundary before adding derived key formats. Signed-off-by: Ralf Lici <[email protected]> --- No functional changes since v1 https://lore.kernel.org/openvpn-devel/1c59dc2e7d63b93d66427eab461e4b1ea7dfcd20.1782919654.git.r...@mandelbit.com/ drivers/net/ovpn/crypto.h | 28 ++++++----- drivers/net/ovpn/crypto_aead.c | 56 +++++++++++----------- drivers/net/ovpn/crypto_aead.h | 2 +- drivers/net/ovpn/crypto_key.c | 85 ++++++++++++++++++++++++---------- drivers/net/ovpn/io.c | 9 ++-- drivers/net/ovpn/pktid.h | 10 ++-- 6 files changed, 118 insertions(+), 72 deletions(-) diff --git a/drivers/net/ovpn/crypto.h b/drivers/net/ovpn/crypto.h index 3b21b42a25eb..c36cd8299afd 100644 --- a/drivers/net/ovpn/crypto.h +++ b/drivers/net/ovpn/crypto.h @@ -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; }; diff --git a/drivers/net/ovpn/crypto_aead.c b/drivers/net/ovpn/crypto_aead.c index 5306c0d2b5c8..31f5327694d2 100644 --- a/drivers/net/ovpn/crypto_aead.c +++ b/drivers/net/ovpn/crypto_aead.c @@ -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); diff --git a/drivers/net/ovpn/crypto_aead.h b/drivers/net/ovpn/crypto_aead.h index 57a7b88cd6c5..4f83a3aa37fd 100644 --- a/drivers/net/ovpn/crypto_aead.h +++ b/drivers/net/ovpn/crypto_aead.h @@ -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_ */ diff --git a/drivers/net/ovpn/crypto_key.c b/drivers/net/ovpn/crypto_key.c index 08fce700811f..cdf35e2b241c 100644 --- a/drivers/net/ovpn/crypto_key.c +++ b/drivers/net/ovpn/crypto_key.c @@ -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; diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index 6f3396f6f72e..2b1df68a1b1e 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -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); diff --git a/drivers/net/ovpn/pktid.h b/drivers/net/ovpn/pktid.h index a85a1d160150..235c9111d085 100644 --- a/drivers/net/ovpn/pktid.h +++ b/drivers/net/ovpn/pktid.h @@ -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); -- 2.54.0 _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel
