]> www.infradead.org Git - users/hch/misc.git/commitdiff
psp: track generations of device key
authorJakub Kicinski <kuba@kernel.org>
Wed, 17 Sep 2025 00:09:37 +0000 (17:09 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 18 Sep 2025 10:32:06 +0000 (12:32 +0200)
There is a (somewhat theoretical in absence of multi-host support)
possibility that another entity will rotate the key and we won't
know. This may lead to accepting packets with matching SPI but
which used different crypto keys than we expected.

The PSP Architecture specification mentions that an implementation
should track device key generation when device keys are managed by the
NIC. Some PSP implementations may opt to include this key generation
state in decryption metadata each time a device key is used to decrypt
a packet. If that is the case, that key generation counter can also be
used when policy checking a decrypted skb against a psp_assoc. This is
an optional feature that is not explicitly part of the PSP spec, but
can provide additional security in the case where an attacker may have
the ability to force key rotations faster than rekeying can occur.

Since we're tracking "key generations" more explicitly now,
maintain different lists for associations from different generations.
This way we can catch stale associations (the user space should
listen to rotation notifications and change the keys).

Drivers can "opt out" of generation tracking by setting
the generation value to 0.

Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Daniel Zahka <daniel.zahka@gmail.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20250917000954.859376-11-daniel.zahka@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
include/net/psp/types.h
net/psp/psp.h
net/psp/psp_main.c
net/psp/psp_nl.c
net/psp/psp_sock.c

index f93ad0e6c04f8674a5d39ebd87a87fdd7f76ab8c..ec218747ced07e199c04bb12b2b7e1f674c224d4 100644 (file)
@@ -50,8 +50,12 @@ struct psp_dev_config {
  * @lock:      instance lock, protects all fields
  * @refcnt:    reference count for the instance
  * @id:                instance id
+ * @generation:        current generation of the device key
  * @config:    current device configuration
  * @active_assocs:     list of registered associations
+ * @prev_assocs:       associations which use old (but still usable)
+ *                     device key
+ * @stale_assocs:      associations which use a rotated out key
  *
  * @rcu:       RCU head for freeing the structure
  */
@@ -67,13 +71,19 @@ struct psp_dev {
 
        u32 id;
 
+       u8 generation;
+
        struct psp_dev_config config;
 
        struct list_head active_assocs;
+       struct list_head prev_assocs;
+       struct list_head stale_assocs;
 
        struct rcu_head rcu;
 };
 
+#define PSP_GEN_VALID_MASK     0x7f
+
 /**
  * struct psp_dev_caps - PSP device capabilities
  */
index defd3e3fd5e73c7747c230c96a18bfb45cb326db..0f34e1a23fdd553861d411c960bc41ebe534a688 100644 (file)
@@ -27,6 +27,7 @@ int psp_sock_assoc_set_rx(struct sock *sk, struct psp_assoc *pas,
 int psp_sock_assoc_set_tx(struct sock *sk, struct psp_dev *psd,
                          u32 version, struct psp_key_parsed *key,
                          struct netlink_ext_ack *extack);
+void psp_assocs_key_rotated(struct psp_dev *psd);
 
 static inline void psp_dev_get(struct psp_dev *psd)
 {
index a1ae3c8920c3249695115a891e989fbe71d0aa87..98ad8c85b58e65bd0d22c93418f9d379275404d3 100644 (file)
@@ -72,6 +72,8 @@ psp_dev_create(struct net_device *netdev,
 
        mutex_init(&psd->lock);
        INIT_LIST_HEAD(&psd->active_assocs);
+       INIT_LIST_HEAD(&psd->prev_assocs);
+       INIT_LIST_HEAD(&psd->stale_assocs);
        refcount_set(&psd->refcnt, 1);
 
        mutex_lock(&psp_devs_lock);
@@ -125,7 +127,9 @@ void psp_dev_unregister(struct psp_dev *psd)
        xa_store(&psp_devs, psd->id, NULL, GFP_KERNEL);
        mutex_unlock(&psp_devs_lock);
 
-       list_for_each_entry_safe(pas, next, &psd->active_assocs, assocs_list)
+       list_splice_init(&psd->active_assocs, &psd->prev_assocs);
+       list_splice_init(&psd->prev_assocs, &psd->stale_assocs);
+       list_for_each_entry_safe(pas, next, &psd->stale_assocs, assocs_list)
                psp_dev_tx_key_del(psd, pas);
 
        rcu_assign_pointer(psd->main_netdev->psp_dev, NULL);
index 1b1d08fce637c6b55d313b8644b15d5b3fffb506..8aaca62744c3c19bc28f3afa25d0d5f5696fb3c3 100644 (file)
@@ -230,6 +230,7 @@ int psp_nl_key_rotate_doit(struct sk_buff *skb, struct genl_info *info)
        struct psp_dev *psd = info->user_ptr[0];
        struct genl_info ntf_info;
        struct sk_buff *ntf, *rsp;
+       u8 prev_gen;
        int err;
 
        rsp = psp_nl_reply_new(info);
@@ -249,10 +250,19 @@ int psp_nl_key_rotate_doit(struct sk_buff *skb, struct genl_info *info)
                goto err_free_ntf;
        }
 
+       /* suggest the next gen number, driver can override */
+       prev_gen = psd->generation;
+       psd->generation = (prev_gen + 1) & PSP_GEN_VALID_MASK;
+
        err = psd->ops->key_rotate(psd, info->extack);
        if (err)
                goto err_free_ntf;
 
+       WARN_ON_ONCE((psd->generation && psd->generation == prev_gen) ||
+                    psd->generation & ~PSP_GEN_VALID_MASK);
+
+       psp_assocs_key_rotated(psd);
+
        nlmsg_end(ntf, (struct nlmsghdr *)ntf->data);
        genlmsg_multicast_netns(&psp_nl_family, dev_net(psd->main_netdev), ntf,
                                0, PSP_NLGRP_USE, GFP_KERNEL);
index 10e1fda30aa0a35a72f411456fe73e68aad8b625..afa966c6b69dc657a54425656131b57645c81f6f 100644 (file)
@@ -60,6 +60,7 @@ struct psp_assoc *psp_assoc_create(struct psp_dev *psd)
 
        pas->psd = psd;
        pas->dev_id = psd->id;
+       pas->generation = psd->generation;
        psp_dev_get(psd);
        refcount_set(&pas->refcnt, 1);
 
@@ -248,6 +249,21 @@ exit_unlock:
        return err;
 }
 
+void psp_assocs_key_rotated(struct psp_dev *psd)
+{
+       struct psp_assoc *pas, *next;
+
+       /* Mark the stale associations as invalid, they will no longer
+        * be able to Rx any traffic.
+        */
+       list_for_each_entry_safe(pas, next, &psd->prev_assocs, assocs_list)
+               pas->generation |= ~PSP_GEN_VALID_MASK;
+       list_splice_init(&psd->prev_assocs, &psd->stale_assocs);
+       list_splice_init(&psd->active_assocs, &psd->prev_assocs);
+
+       /* TODO: we should inform the sockets that got shut down */
+}
+
 void psp_twsk_init(struct inet_timewait_sock *tw, const struct sock *sk)
 {
        struct psp_assoc *pas = psp_sk_assoc(sk);