mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] xfrm: esp4: use current ESN high bits for IV and AAD
@ 2026-09-25  9:51 Jérémy Jean
  2026-09-29  3:52 ` netdev-bot+sashiko
  0 siblings, 1 reply; 2+ messages in thread
From: Jérémy Jean @ 2026-09-25  9:51 UTC (permalink / raw)
  To: Steffen Klassert, Herbert Xu, David S. Miller
  Cc: netdev, linux-kernel, Jérémy Jean

esp_xmit() saves the low half of the current packet sequence number
before advancing the GSO sequence state, but builds esp.seqno with the
high half after the advance. When the low half wraps, the packet whose
transmitted sequence number is 0xffffffff is encrypted as though it
belonged to the next sequence-number cycle. With AES-GCM, this assigns
the boundary packet the same nonce as the packet sent one complete
32-bit sequence-number cycle later. esp_output_set_extra() also reads
the advanced high half when constructing the associated data, so the
two packets use identical associated data.

Because GCM uses CTR mode for encryption, known plaintext from either
record reveals the corresponding plaintext in the other. More
importantly, reusing the nonce makes the GHASH authentication key
recoverable, allowing an attacker to forge valid tags for arbitrary
ciphertexts under that nonce and key.

Starting from sequence number zero, reaching the faulty packet requires
2^32 - 1 outbound ESP packet sequence increments. Reusing its nonce
requires another 2^32 increments under the same AES-GCM key, for
2^33 - 1 increments in total.

Snapshot both halves of the current sequence before changing the GSO
state. Derive the authenticated high half from that same immutable
sequence value so the IV and associated data cannot diverge.

Fixes: 4b549ccce941 ("xfrm: replay: Fix ESN wrap around for GSO")
Assisted-by: LLM
Signed-off-by: Jérémy Jean <Jeremy.Jean@oss.cyber.gouv.fr>
---
 net/ipv4/esp4.c         | 13 ++++---------
 net/ipv4/esp4_offload.c |  6 ++++--
 2 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index e76db5817e78..04f27c41ea50 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -271,20 +271,15 @@ static void esp_output_restore_header(struct sk_buff *skb)
 static struct ip_esp_hdr *esp_output_set_extra(struct sk_buff *skb,
 					       struct xfrm_state *x,
 					       struct ip_esp_hdr *esph,
-					       struct esp_output_extra *extra)
+					       struct esp_output_extra *extra,
+					       __be64 seqno)
 {
 	/* For ESN we move the header forward by 4 bytes to
 	 * accommodate the high bits.  We will move it back after
 	 * encryption.
 	 */
 	if ((x->props.flags & XFRM_STATE_ESN)) {
-		__u32 seqhi;
-		struct xfrm_offload *xo = xfrm_offload(skb);
-
-		if (xo)
-			seqhi = xo->seq.hi;
-		else
-			seqhi = XFRM_SKB_CB(skb)->seq.output.hi;
+		__u32 seqhi = upper_32_bits(be64_to_cpu(seqno));
 
 		extra->esphoff = (unsigned char *)esph -
 				 skb_transport_header(skb);
@@ -543,7 +538,7 @@ int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *
 	else
 		dsg = &sg[esp->nfrags];
 
-	esph = esp_output_set_extra(skb, x, esp->esph, extra);
+	esph = esp_output_set_extra(skb, x, esp->esph, extra, esp->seqno);
 	esp->esph = esph;
 
 	sg_init_table(sg, esp->nfrags);
diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c
index abd77162f5e7..79f7d08325c5 100644
--- a/net/ipv4/esp4_offload.c
+++ b/net/ipv4/esp4_offload.c
@@ -272,7 +272,7 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb,  netdev_features_
 	struct crypto_aead *aead;
 	struct esp_info esp;
 	bool hw_offload = true;
-	__u32 seq;
+	__u32 seq, seq_hi;
 	int encap_type = 0;
 
 	esp.inplace = true;
@@ -315,7 +315,9 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb,  netdev_features_
 			return esp.nfrags;
 	}
 
+	/* Keep the sequence used by this packet before advancing GSO state. */
 	seq = xo->seq.low;
+	seq_hi = xo->seq.hi;
 
 	esph = esp.esph;
 	esph->spi = x->id.spi;
@@ -334,7 +336,7 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb,  netdev_features_
 	if (xo->seq.low < seq)
 		xo->seq.hi++;
 
-	esp.seqno = cpu_to_be64(seq + ((u64)xo->seq.hi << 32));
+	esp.seqno = cpu_to_be64(seq + ((u64)seq_hi << 32));
 
 	if (hw_offload && encap_type == UDP_ENCAP_ESPINUDP) {
 		/* In the XFRM stack, the encapsulation protocol is set to iphdr->protocol by
-- 
2.47.3


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-29  3:52 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-25  9:51 [PATCH] xfrm: esp4: use current ESN high bits for IV and AAD Jérémy Jean
2026-09-29  3:52 ` netdev-bot+sashiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®