@@ -16,6 +16,10 @@ definitions:
type: const
name: nonce-tail-size
value: 8
+ -
+ type: const
+ name: epoch-prk-size
+ value: 32
-
type: enum
name: cipher-alg
@@ -274,6 +278,16 @@ attribute-sets:
type: nest
doc: Key material for decrypt direction
nested-attributes: keydir
+ -
+ name: encrypt-epoch
+ type: nest
+ doc: Epoch PRK material used to derive encrypt data keys
+ nested-attributes: epoch
+ -
+ name: decrypt-epoch
+ type: nest
+ doc: Epoch PRK material used to derive decrypt data keys
+ nested-attributes: epoch
-
name: keydir
attributes:
@@ -291,6 +305,23 @@ attribute-sets:
obtain the actual cipher IV
checks:
exact-len: nonce-tail-size
+ -
+ name: epoch
+ attributes:
+ -
+ name: key
+ type: binary
+ doc: >-
+ The epoch PRK used to derive cipher keys and implicit IVs
+ checks:
+ exact-len: epoch-prk-size
+ -
+ name: cipher-key-len
+ type: u32
+ doc: Length in bytes of the AEAD cipher key derived from the epoch PRK
+ checks:
+ min: 1
+ max: epoch-prk-size
-
name: keyconf-get
@@ -16,7 +16,6 @@
#include <linux/types.h>
#include <uapi/linux/ovpn.h>
-#define OVPN_EPOCH_PRK_SIZE 32
#define OVPN_MAX_EPOCH U16_MAX
#define OVPN_EPOCH_FUTURE_KEYS_COUNT 16
@@ -25,13 +25,20 @@ static const struct netlink_range_validation ovpn_a_keyconf_peer_id_range = {
};
/* Common nested types */
-const struct nla_policy ovpn_keyconf_nl_policy[OVPN_A_KEYCONF_DECRYPT_DIR + 1] = {
+const struct nla_policy ovpn_epoch_nl_policy[OVPN_A_EPOCH_CIPHER_KEY_LEN + 1] = {
+ [OVPN_A_EPOCH_KEY] = NLA_POLICY_EXACT_LEN(OVPN_EPOCH_PRK_SIZE),
+ [OVPN_A_EPOCH_CIPHER_KEY_LEN] = NLA_POLICY_RANGE(NLA_U32, 1, OVPN_EPOCH_PRK_SIZE),
+};
+
+const struct nla_policy ovpn_keyconf_nl_policy[OVPN_A_KEYCONF_DECRYPT_EPOCH + 1] = {
[OVPN_A_KEYCONF_PEER_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_keyconf_peer_id_range),
[OVPN_A_KEYCONF_SLOT] = NLA_POLICY_MAX(NLA_U32, 1),
[OVPN_A_KEYCONF_KEY_ID] = NLA_POLICY_MAX(NLA_U32, 7),
[OVPN_A_KEYCONF_CIPHER_ALG] = NLA_POLICY_MAX(NLA_U32, 2),
[OVPN_A_KEYCONF_ENCRYPT_DIR] = NLA_POLICY_NESTED(ovpn_keydir_nl_policy),
[OVPN_A_KEYCONF_DECRYPT_DIR] = NLA_POLICY_NESTED(ovpn_keydir_nl_policy),
+ [OVPN_A_KEYCONF_ENCRYPT_EPOCH] = NLA_POLICY_NESTED(ovpn_epoch_nl_policy),
+ [OVPN_A_KEYCONF_DECRYPT_EPOCH] = NLA_POLICY_NESTED(ovpn_epoch_nl_policy),
};
const struct nla_policy ovpn_keyconf_del_input_nl_policy[OVPN_A_KEYCONF_SLOT + 1] = {
@@ -13,7 +13,8 @@
#include <uapi/linux/ovpn.h>
/* Common nested types */
-extern const struct nla_policy ovpn_keyconf_nl_policy[OVPN_A_KEYCONF_DECRYPT_DIR + 1];
+extern const struct nla_policy ovpn_epoch_nl_policy[OVPN_A_EPOCH_CIPHER_KEY_LEN + 1];
+extern const struct nla_policy ovpn_keyconf_nl_policy[OVPN_A_KEYCONF_DECRYPT_EPOCH + 1];
extern const struct nla_policy ovpn_keyconf_del_input_nl_policy[OVPN_A_KEYCONF_SLOT + 1];
extern const struct nla_policy ovpn_keyconf_get_nl_policy[OVPN_A_KEYCONF_CIPHER_ALG + 1];
extern const struct nla_policy ovpn_keyconf_swap_input_nl_policy[OVPN_A_KEYCONF_PEER_ID + 1];
@@ -859,6 +859,40 @@ static int ovpn_nl_get_key_dir(struct genl_info *info, struct nlattr *key,
return 0;
}
+static int ovpn_nl_get_epoch_key(struct genl_info *info, struct nlattr *key,
+ enum ovpn_cipher_alg cipher,
+ struct ovpn_epoch_prk *prk)
+{
+ struct nlattr *attrs[OVPN_A_EPOCH_MAX + 1];
+ int ret;
+
+ ret = nla_parse_nested(attrs, OVPN_A_EPOCH_MAX, key,
+ ovpn_epoch_nl_policy, info->extack);
+ if (ret)
+ return ret;
+
+ switch (cipher) {
+ case OVPN_CIPHER_ALG_AES_GCM:
+ case OVPN_CIPHER_ALG_CHACHA20_POLY1305:
+ if (NL_REQ_ATTR_CHECK(info->extack, key, attrs,
+ OVPN_A_EPOCH_KEY) ||
+ NL_REQ_ATTR_CHECK(info->extack, key, attrs,
+ OVPN_A_EPOCH_CIPHER_KEY_LEN))
+ return -EINVAL;
+
+ prk->key = nla_data(attrs[OVPN_A_EPOCH_KEY]);
+ prk->key_size = nla_len(attrs[OVPN_A_EPOCH_KEY]);
+ prk->cipher_key_len =
+ nla_get_u32(attrs[OVPN_A_EPOCH_CIPHER_KEY_LEN]);
+ break;
+ default:
+ NL_SET_ERR_MSG_MOD(info->extack, "unsupported cipher");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/**
* ovpn_nl_key_new_doit - configure a new key for the specified peer
* @skb: incoming netlink message
@@ -889,6 +923,7 @@ int ovpn_nl_key_new_doit(struct sk_buff *skb, struct genl_info *info)
struct nlattr *attrs[OVPN_A_KEYCONF_MAX + 1];
struct ovpn_priv *ovpn = info->user_ptr[0];
struct ovpn_peer_key_reset pkr = {};
+ bool direct_key, epoch_key;
struct ovpn_peer *peer;
u32 peer_id;
int ret;
@@ -911,28 +946,70 @@ int ovpn_nl_key_new_doit(struct sk_buff *skb, struct genl_info *info)
NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_KEYCONF], attrs,
OVPN_A_KEYCONF_KEY_ID) ||
NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_KEYCONF], attrs,
- OVPN_A_KEYCONF_CIPHER_ALG) ||
- NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_KEYCONF], attrs,
- OVPN_A_KEYCONF_ENCRYPT_DIR) ||
- NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_KEYCONF], attrs,
- OVPN_A_KEYCONF_DECRYPT_DIR))
+ OVPN_A_KEYCONF_CIPHER_ALG))
return -EINVAL;
pkr.slot = nla_get_u32(attrs[OVPN_A_KEYCONF_SLOT]);
pkr.key.key_id = nla_get_u32(attrs[OVPN_A_KEYCONF_KEY_ID]);
pkr.key.cipher_alg = nla_get_u32(attrs[OVPN_A_KEYCONF_CIPHER_ALG]);
- ret = ovpn_nl_get_key_dir(info, attrs[OVPN_A_KEYCONF_ENCRYPT_DIR],
- pkr.key.cipher_alg,
- &pkr.key.direct.encrypt);
- if (ret < 0)
- return ret;
+ direct_key = attrs[OVPN_A_KEYCONF_ENCRYPT_DIR] ||
+ attrs[OVPN_A_KEYCONF_DECRYPT_DIR];
+ epoch_key = attrs[OVPN_A_KEYCONF_ENCRYPT_EPOCH] ||
+ attrs[OVPN_A_KEYCONF_DECRYPT_EPOCH];
+ if (direct_key == epoch_key) {
+ NL_SET_ERR_MSG_MOD(info->extack,
+ "specify exactly one key format");
+ return -EINVAL;
+ }
- ret = ovpn_nl_get_key_dir(info, attrs[OVPN_A_KEYCONF_DECRYPT_DIR],
- pkr.key.cipher_alg,
- &pkr.key.direct.decrypt);
- if (ret < 0)
- return ret;
+ if (direct_key) {
+ if (NL_REQ_ATTR_CHECK(info->extack,
+ info->attrs[OVPN_A_KEYCONF], attrs,
+ OVPN_A_KEYCONF_ENCRYPT_DIR) ||
+ NL_REQ_ATTR_CHECK(info->extack,
+ info->attrs[OVPN_A_KEYCONF], attrs,
+ OVPN_A_KEYCONF_DECRYPT_DIR))
+ return -EINVAL;
+
+ pkr.key.use_epoch_keys = false;
+ ret = ovpn_nl_get_key_dir(info,
+ attrs[OVPN_A_KEYCONF_ENCRYPT_DIR],
+ pkr.key.cipher_alg,
+ &pkr.key.direct.encrypt);
+ if (ret < 0)
+ return ret;
+
+ ret = ovpn_nl_get_key_dir(info,
+ attrs[OVPN_A_KEYCONF_DECRYPT_DIR],
+ pkr.key.cipher_alg,
+ &pkr.key.direct.decrypt);
+ if (ret < 0)
+ return ret;
+ } else {
+ if (NL_REQ_ATTR_CHECK(info->extack,
+ info->attrs[OVPN_A_KEYCONF], attrs,
+ OVPN_A_KEYCONF_ENCRYPT_EPOCH) ||
+ NL_REQ_ATTR_CHECK(info->extack,
+ info->attrs[OVPN_A_KEYCONF], attrs,
+ OVPN_A_KEYCONF_DECRYPT_EPOCH))
+ return -EINVAL;
+
+ pkr.key.use_epoch_keys = true;
+ ret = ovpn_nl_get_epoch_key(info,
+ attrs[OVPN_A_KEYCONF_ENCRYPT_EPOCH],
+ pkr.key.cipher_alg,
+ &pkr.key.epoch.encrypt);
+ if (ret < 0)
+ return ret;
+
+ ret = ovpn_nl_get_epoch_key(info,
+ attrs[OVPN_A_KEYCONF_DECRYPT_EPOCH],
+ pkr.key.cipher_alg,
+ &pkr.key.epoch.decrypt);
+ if (ret < 0)
+ return ret;
+ }
peer_id = nla_get_u32(attrs[OVPN_A_KEYCONF_PEER_ID]);
peer = ovpn_peer_get_by_id(ovpn, peer_id);
@@ -11,6 +11,7 @@
#define OVPN_FAMILY_VERSION 1
#define OVPN_NONCE_TAIL_SIZE 8
+#define OVPN_EPOCH_PRK_SIZE 32
enum ovpn_cipher_alg {
OVPN_CIPHER_ALG_NONE,
@@ -68,6 +69,8 @@ enum {
OVPN_A_KEYCONF_CIPHER_ALG,
OVPN_A_KEYCONF_ENCRYPT_DIR,
OVPN_A_KEYCONF_DECRYPT_DIR,
+ OVPN_A_KEYCONF_ENCRYPT_EPOCH,
+ OVPN_A_KEYCONF_DECRYPT_EPOCH,
__OVPN_A_KEYCONF_MAX,
OVPN_A_KEYCONF_MAX = (__OVPN_A_KEYCONF_MAX - 1)
@@ -81,6 +84,14 @@ enum {
OVPN_A_KEYDIR_MAX = (__OVPN_A_KEYDIR_MAX - 1)
};
+enum {
+ OVPN_A_EPOCH_KEY = 1,
+ OVPN_A_EPOCH_CIPHER_KEY_LEN,
+
+ __OVPN_A_EPOCH_MAX,
+ OVPN_A_EPOCH_MAX = (__OVPN_A_EPOCH_MAX - 1)
+};
+
enum {
OVPN_A_IFINDEX = 1,
OVPN_A_PEER,